diff --git a/src/app/[spaceId]/.gitbook/pdf/page.tsx b/src/app/[spaceId]/.gitbook/pdf/page.tsx
new file mode 100644
index 000000000..d67080f9f
--- /dev/null
+++ b/src/app/[spaceId]/.gitbook/pdf/page.tsx
@@ -0,0 +1,112 @@
+import * as React from 'react';
+
+import { api } from '@/lib/api';
+import { resolvePageId } from '@/lib/pages';
+import { DocumentView } from '@/components/DocumentView';
+
+import { SpaceParams } from '../../fetch';
+import { Revision, RevisionPageDocument, RevisionPageGroup, Space } from '@gitbook/api';
+import { notFound } from 'next/navigation';
+
+interface PDFSearchParams {
+ /** Page to export. If none is passed, all pages are exported. */
+ page?: string;
+ /** If true, only the `page` is exported, and not its descendant */
+ only?: boolean;
+}
+
+
+/**
+ * Render a space as a standalone HTML page without interactive elements.
+ * The HTML can be converted to PDF.
+ */
+export default async function PDFHTMLOutput(props: { params: SpaceParams; searchParams: PDFSearchParams }) {
+ const { params, searchParams } = props;
+ const { spaceId } = params;
+
+ const [{ data: space }, { data: revision }] = await Promise.all([
+ api().spaces.getSpaceById(spaceId),
+ api().spaces.getCurrentRevision(spaceId),
+ ]);
+
+ const pages = selectPages(revision, searchParams).slice(0, 4); // TODO: remove slice
+
+ return (
+ <>
+ {pages.map(({ page, depth }) => (
+ page.type === 'group' ?