diff --git a/.gitignore b/.gitignore
index 10d45566..d102c5ed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -48,4 +48,5 @@ package-lock.json
results.txt
# Next.js
-next-env.d.ts
\ No newline at end of file
+next-env.d.ts
+*.tsbuildinfo
diff --git a/apps/website/README.md b/apps/website/README.md
index ceda0a60..774f323d 100644
--- a/apps/website/README.md
+++ b/apps/website/README.md
@@ -1,217 +1,49 @@
-# Geistdocs
+# Streamdown Docs Website
-A modern documentation template built with Next.js and [Fumadocs](https://fumadocs.dev). Designed for spinning up Vercel documentation sites quickly and consistently with built-in AI chat, GitHub discussions integration, and a beautiful UI.
+The documentation and marketing site for [Streamdown](https://streamdown.ai), built on [Geistdocs](https://github.com/vercel/geistdocs) β Vercel's package-backed docs system powered by Next.js and [Fumadocs](https://fumadocs.dev).
-## Features
+The shared runtime (layout, docs renderer, search, Ask AI, markdown routes, proxy behavior, and MDX components) is provided by [`@vercel/geistdocs`](https://www.npmjs.com/package/@vercel/geistdocs). This app owns its content, configuration, and thin adapter files.
-- π **MDX-powered documentation** - Write docs in MDX with full component support
-- π€ **AI-powered chat** - Built-in AI assistant that understands your documentation
-- π¬ **GitHub Discussions integration** - Allow users to provide feedback directly to GitHub
-- π¨ **Modern UI** - Beautiful, accessible components built with Radix UI
-- π **Advanced search** - Fast, fuzzy search through all documentation
-- π **Dark mode** - Built-in theme switching
-- π± **Responsive** - Mobile-first design that works everywhere
-- β‘ **Fast** - Built on Next.js 16 with App Router for optimal performance
-- π° **RSS** - Built-in RSS feed for your documentation
+## Structure
-## Getting Started
+- `content/docs/` - Documentation pages (MDX). Sidebar order is controlled by `content/docs/meta.json`.
+- `geistdocs.tsx` - Site configuration (logo, nav, GitHub links, AI prompt and suggestions, agent metadata).
+- `lib/geistdocs/` - Source and config adapters wiring the app to `@vercel/geistdocs`.
+- `components/geistdocs/` - User-owned adapters for MDX components, the provider, and the docs layout.
+- `app/[lang]/(home)/` - The marketing landing page.
+- `app/[lang]/playground/` - The interactive Streamdown playground.
+- `app/[lang]/docs/` - Docs routes rendered by `createDocsPage` from the package.
+- `proxy.ts` - Markdown content negotiation (`.md`/`.mdx` URLs, `Accept: text/markdown`) and i18n handling via `createProxy`.
-### Prerequisites
+## AI-readable output
-- Node.js 18+ and pnpm (recommended) or npm
-- A GitHub repository for documentation content
-- A GitHub App for discussions integration (optional)
+The site serves machine-readable surfaces from package route helpers:
-### Installation
+- `/llms.txt` - Full documentation corpus as markdown
+- `/sitemap.md` - Markdown sitemap with page summaries
+- `/agents.md` - Agent-readiness metadata
+- Any docs page with a `.md`/`.mdx` suffix - Per-page markdown
-1. Create a new repository using this template:
-
-```bash
-gh repo create mydocs --template vercel/geistdocs --clone
-cd mydocs
-```
+## Development
-2. Install dependencies:
+From the repository root:
```bash
pnpm install
+pnpm dev --filter website
```
-3. Configure environment variables (see [Environment Variables](#environment-variables) below)
-
-4. Run the development server:
-
-```bash
-pnpm dev
-```
-
-5. Open [http://localhost:3000](http://localhost:3000) in your browser
-
-## Environment Variables
+The site consumes the workspace `streamdown` and `@streamdown/*` packages directly, so live demos always render against the local source.
-Create a `.env.local` file in the root of your project with the following variables:
-
-### Required Variables
+## Updating Geistdocs
```bash
-# GitHub Repository Configuration
-# The owner of the GitHub repository (organization or username)
-NEXT_PUBLIC_GEISTDOCS_OWNER=your-github-username
-
-# The name of your GitHub repository
-NEXT_PUBLIC_GEISTDOCS_REPO=your-repo-name
-
-# The name of the GitHub Discussions category for feedback
-NEXT_PUBLIC_GEISTDOCS_CATEGORY=Documentation
-
-# GitHub App Credentials (for discussions integration)
-# Create a GitHub App at: https://github.com/settings/apps/new
-GITHUB_APP_ID=your-app-id
-GITHUB_APP_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
-
-# AI Gateway API Key (for AI chat functionality)
-# This is typically an OpenAI API key or compatible AI gateway
-AI_GATEWAY_API_KEY=your-api-key
-
-# Production URL (set automatically on Vercel)
-NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL=yourdomain.com
+npx @vercel/geistdocs update
```
-### Setting Up GitHub App
-
-To enable the feedback feature that creates GitHub Discussions:
-
-1. Go to [GitHub Apps settings](https://github.com/settings/apps/new)
-2. Create a new GitHub App with the following permissions:
- - Repository permissions:
- - Discussions: Read and write
- - Subscribe to events: Discussions
-3. Generate a private key and save it
-4. Install the app on your documentation repository
-5. Add the App ID and private key to your `.env.local`
-
-### Setting Up AI Chat
-
-The AI chat feature uses the Vercel AI SDK with OpenAI:
-
-1. Get an API key from [OpenAI](https://platform.openai.com/api-keys)
-2. Add it as `AI_GATEWAY_API_KEY` in your `.env.local`
-3. The chat will automatically search and answer questions about your documentation
-
-## Project Structure
-
-```
-geistdocs/
-βββ app/
-β βββ (home)/ # Landing page and marketing content
-β βββ docs/ # Documentation pages
-β βββ api/
-β β βββ chat/ # AI chat API endpoint
-β β βββ search/ # Search API endpoint
-β βββ actions/ # Server actions (e.g., GitHub discussions)
-βββ components/
-β βββ geistdocs/ # Custom Geistdocs components
-β βββ ui/ # Reusable UI components
-βββ content/ # MDX documentation content
-βββ lib/
-β βββ source.ts # Content source adapter
-β βββ layout.shared.tsx # Shared layout configuration
-βββ source.config.ts # Fumadocs MDX configuration
-```
-
-## Writing Documentation
-
-Documentation is written in MDX format in the `content/` directory. Each file can include:
-
-- **Frontmatter** - Metadata like title, description, and more
-- **MDX components** - Use React components directly in your markdown
-- **Code blocks** - Syntax-highlighted code with Shiki
-
-Example:
-
-```mdx
----
-title: Getting Started
-description: Learn how to get started with Geistdocs
----
-
-# Getting Started
-
-Welcome to the documentation!
-
-
- This is a custom component you can use in your docs.
-
-```
-
-## Customization
-
-### Styling
-
-The project uses Tailwind CSS 4. Customize the theme in `tailwind.config.ts` and global styles in `app/globals.css`.
-
-### Components
-
-Add custom components to `components/geistdocs/` and import them in your MDX files.
-
-### Layout
-
-Modify the shared layout options in `lib/layout.shared.tsx` to customize the sidebar, navigation, and more.
-
-## Development
-
-```bash
-# Start development server
-pnpm dev
-
-# Build for production
-pnpm build
-
-# Start production server
-pnpm start
-
-# Run linter
-pnpm lint
-
-# Format code
-pnpm format
-```
-
-## Code Quality
-
-This project uses [Ultracite](https://github.com/haydenbleasel/ultracite), a zero-config Biome preset for TypeScript/React projects. All code is automatically formatted and linted with strict rules for:
-
-- Type safety
-- Accessibility
-- Performance
-- React best practices
-
-Run `pnpm lint` to check for issues and `pnpm format` to auto-fix formatting.
-
-## Deployment
-
-### Vercel (Recommended)
-
-[](https://vercel.com/new/clone?repository-url=https://github.com/vercel/geistdocs)
-
-1. Click the "Deploy" button above
-2. Configure your environment variables in the Vercel dashboard
-3. Deploy!
-
-### Other Platforms
-
-This is a standard Next.js application and can be deployed to any platform that supports Node.js:
-
-1. Build the application: `pnpm build`
-2. Start the server: `pnpm start`
-3. Ensure all environment variables are set
-
-## Learn More
-
-- [Next.js Documentation](https://nextjs.org/docs) - Learn about Next.js features and API
-- [Fumadocs](https://fumadocs.dev) - Learn about Fumadocs
-- [Ultracite](https://github.com/haydenbleasel/ultracite) - Learn about code quality standards
+This bumps the `@vercel/geistdocs` dependency. Local adapter files are user-owned and never overwritten.
-## License
+## Environment variables
-MIT
+- `NEXT_PUBLIC_VERCEL_PROJECT_PRODUCTION_URL` - Production URL used for absolute links (set automatically on Vercel)
+- `AI_GATEWAY_API_KEY` - Enables the Ask AI assistant (set automatically on Vercel)
diff --git a/apps/website/app/[lang]/(home)/components/installer.tsx b/apps/website/app/[lang]/(home)/components/installer.tsx
index 03b731fd..d513ff00 100644
--- a/apps/website/app/[lang]/(home)/components/installer.tsx
+++ b/apps/website/app/[lang]/(home)/components/installer.tsx
@@ -12,33 +12,37 @@ import {
InputGroupText,
} from "@/components/ui/input-group";
-const CODE = "npm i streamdown";
-const TIMEOUT = 2000;
+const COPY_TIMEOUT = 2000;
-export const Installer = () => {
+interface InstallerProps {
+ className?: string;
+ command: string;
+}
+
+export const Installer = ({ command, className = "w-48" }: InstallerProps) => {
const [copied, setCopied] = useState(false);
const handleCopy = () => {
- navigator.clipboard.writeText(CODE);
+ navigator.clipboard.writeText(command);
toast.success("Copied to clipboard");
setCopied(true);
track("Copied installer command");
setTimeout(() => {
setCopied(false);
- }, TIMEOUT);
+ }, COPY_TIMEOUT);
};
const Icon = copied ? CheckIcon : CopyIcon;
return (
-
+
$
-
+
) => {
const { lang } = await params;
return (
-
- {children}
-
+
+ {children}
+
);
};
diff --git a/apps/website/app/[lang]/(home)/page.tsx b/apps/website/app/[lang]/(home)/page.tsx
index 02366515..cf2b0ec1 100644
--- a/apps/website/app/[lang]/(home)/page.tsx
+++ b/apps/website/app/[lang]/(home)/page.tsx
@@ -1,11 +1,11 @@
import DynamicLink from "fumadocs-core/dynamic-link";
import type { Metadata } from "next";
-import { Installer } from "@/components/geistdocs/installer";
import { Button } from "@/components/ui/button";
import { CenteredSection } from "./components/centered-section";
import { CTA } from "./components/cta";
import { Demo } from "./components/demo";
import { Hero } from "./components/hero";
+import { Installer } from "./components/installer";
import { Logos } from "./components/logos";
import { OneTwoSection } from "./components/one-two-section";
import { Templates } from "./components/templates";
diff --git a/apps/website/app/[lang]/.well-known/mcp.json/route.ts b/apps/website/app/[lang]/.well-known/mcp.json/route.ts
new file mode 100644
index 00000000..5e1c81eb
--- /dev/null
+++ b/apps/website/app/[lang]/.well-known/mcp.json/route.ts
@@ -0,0 +1,8 @@
+import { createMcpManifestRoute } from "@vercel/geistdocs/routes/mcp";
+import { config } from "@/lib/geistdocs/config";
+
+export const { GET, generateStaticParams, revalidate } = createMcpManifestRoute(
+ {
+ config,
+ }
+);
diff --git a/apps/website/app/[lang]/agents.md/route.ts b/apps/website/app/[lang]/agents.md/route.ts
new file mode 100644
index 00000000..ca644fab
--- /dev/null
+++ b/apps/website/app/[lang]/agents.md/route.ts
@@ -0,0 +1,6 @@
+import { createAgentsRoute } from "@vercel/geistdocs/routes/agents";
+import { config } from "@/lib/geistdocs/config";
+
+export const { GET, generateStaticParams, revalidate } = createAgentsRoute({
+ config,
+});
diff --git a/apps/website/app/[lang]/docs/[[...slug]]/page.tsx b/apps/website/app/[lang]/docs/[[...slug]]/page.tsx
index 35022fae..7ca246b1 100644
--- a/apps/website/app/[lang]/docs/[[...slug]]/page.tsx
+++ b/apps/website/app/[lang]/docs/[[...slug]]/page.tsx
@@ -1,93 +1,22 @@
-import { createRelativeLink } from "fumadocs-ui/mdx";
-import type { Metadata } from "next";
-import { notFound } from "next/navigation";
-import { AskAI } from "@/components/geistdocs/ask-ai";
-import { CopyPage } from "@/components/geistdocs/copy-page";
-import {
- DocsBody,
- DocsDescription,
- DocsPage,
- DocsTitle,
-} from "@/components/geistdocs/docs-page";
-import { EditSource } from "@/components/geistdocs/edit-source";
-import { Feedback } from "@/components/geistdocs/feedback";
+import { MobileDocsBar } from "@vercel/geistdocs/mobile-docs-bar";
+import { createDocsPage } from "@vercel/geistdocs/pages/docs";
import { getMDXComponents } from "@/components/geistdocs/mdx-components";
-import { OpenInChat } from "@/components/geistdocs/open-in-chat";
-import { ScrollTop } from "@/components/geistdocs/scroll-top";
-import { Separator } from "@/components/ui/separator";
-import { getLLMText, getPageImage, source } from "@/lib/geistdocs/source";
-
-const Page = async ({ params }: PageProps<"/[lang]/docs/[[...slug]]">) => {
- const { slug, lang } = await params;
- const page = source.getPage(slug, lang);
-
- if (!page) {
- notFound();
- }
-
- const markdown = await getLLMText(page);
- const MDX = page.data.body;
-
- return (
-
-
-
-
-
-
-
-
-
- ),
- }}
- toc={page.data.toc}
- >
- {page.data.title}
- {page.data.description}
-
-
-
-
- );
-};
-
-export const generateStaticParams = () => source.generateParams();
-
-export const generateMetadata = async ({
- params,
-}: PageProps<"/[lang]/docs/[[...slug]]">) => {
- const { slug, lang } = await params;
- const page = source.getPage(slug, lang);
-
- if (!page) {
- notFound();
- }
-
- const metadata: Metadata = {
- title: page.data.title,
- description: page.data.description,
- openGraph: {
- images: getPageImage(page).url,
- },
- alternates: {
- types: {
- "text/markdown": slug ? `/docs/${slug.join("/")}.md` : "/docs.md",
- },
- },
- };
-
- return metadata;
-};
-
-export default Page;
+import { config } from "@/lib/geistdocs/config";
+import { geistdocsSource } from "@/lib/geistdocs/source";
+
+const docsPage = createDocsPage({
+ config,
+ mdx: ({ link }) => getMDXComponents({ a: link }),
+ openGraph: {
+ images: true,
+ },
+ source: geistdocsSource,
+ tableOfContentPopover: {
+ enabled: false,
+ },
+ renderTop: ({ data }) => ,
+});
+
+export default docsPage.Page;
+export const generateStaticParams = docsPage.generateStaticParams;
+export const generateMetadata = docsPage.generateMetadata;
diff --git a/apps/website/app/[lang]/docs/layout.tsx b/apps/website/app/[lang]/docs/layout.tsx
index df146f1e..0263099d 100644
--- a/apps/website/app/[lang]/docs/layout.tsx
+++ b/apps/website/app/[lang]/docs/layout.tsx
@@ -4,7 +4,11 @@ import { source } from "@/lib/geistdocs/source";
const Layout = async ({ children, params }: LayoutProps<"/[lang]/docs">) => {
const { lang } = await params;
- return {children};
+ return (
+
+ {children}
+
+ );
};
export default Layout;
diff --git a/apps/website/app/[lang]/layout.tsx b/apps/website/app/[lang]/layout.tsx
index 53a8da85..178d39f3 100644
--- a/apps/website/app/[lang]/layout.tsx
+++ b/apps/website/app/[lang]/layout.tsx
@@ -1,10 +1,10 @@
import "../global.css";
import "katex/dist/katex.css";
import "streamdown/styles.css";
-import { Footer } from "@/components/geistdocs/footer";
-import { Navbar } from "@/components/geistdocs/navbar";
+import { Footer } from "@vercel/geistdocs/footer";
+import { Navbar } from "@vercel/geistdocs/navbar";
import { GeistdocsProvider } from "@/components/geistdocs/provider";
-import { basePath } from "@/geistdocs";
+import { config } from "@/lib/geistdocs/config";
import { mono, sans } from "@/lib/geistdocs/fonts";
import { cn } from "@/lib/utils";
@@ -18,10 +18,10 @@ const Layout = async ({ children, params }: LayoutProps<"/[lang]">) => {
suppressHydrationWarning
>
-
-
+
+
{children}
-
+