Start a Fumadocs Blog in 10 Minutes
Building in public? Sometimes you need more than X posts to document your journey; or to create comprehensive product documentation for your users. Here's how to set up a beautiful, fast documentation or blogging site with Fumadocs.
Why Fumadocs?
Most documentation frameworks either look dated or are overkill for indie builders. Fumadocs is lightweight yet powerful enough for serious projects.
| Benefit | Why It Matters |
|---|---|
| Built on React/Next.js | Use the tools you already know, full control over customization |
| MDX Support | Write in Markdown, embed React components when needed |
| Beautiful by Default | Professional UI out of the box - no design skills required |
| Fast Performance | Static generation + incremental builds = instant page loads |
| Rich Components | Steps, tabs, callouts, accordions, cards - all built-in and ready to use |
| SEO Optimized | Metadata, sitemap, Open Graph - everything you need for discoverability |
Perfect for Building in Public
Fumadocs is designed for technical content. Code blocks, API references, step-by-step guides - it handles them all beautifully.
Quick Start
Get your Fumadocs blog running in 10 minutes:
Create your project
pnpm create fumadocs-appnpx create-fumadocs-app@latestyarn create fumadocs-appThe CLI will ask you a few questions:
Project name
- Enter your project name (e.g.,
my-docs,my-blog)
Choose a template
- Next.js: Fumadocs MDX (with RSC)
- Waku: Fumadocs MDX
- React Router: Fumadocs MDX (not RSC)
- React Router SPA: Fumadocs MDX (not RSC)
- Tanstack Start: Fumadocs MDX (not RSC)
- Tanstack Start SPA: Fumadocs MDX (not RSC)
I am using Tanstack Start with Server Side Rendering on Cloudflare Workers. But you can use any other framework you like.
Use /src directory? (on Next.js)
- Yes - Use
/srcdirectory - No - Use
/appdirectory
Configure linter?
- Disabled - Start simple
- ESLint or Biome - Add later if needed
Choose a search solution?
- Default (local search powered by Orama, recommended) - Works offline, no external dependencies
- Orama Cloud - Cloud-based search with better performance for large sites
Install packages automatically?
- Yes - Installs dependencies for you
- No - You'll need to run
pnpm installmanually
Project structure
After creation, you'll have this structure:
my-docs/
├── content/
│ └── docs/ # Your content here
│ ├── index.mdx # Landing page
│ └── meta.json # Navigation structure
├── src/ # Application routes (Next.js)
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Homepage
│ └── [[...slug]]/ # Dynamic doc routes
├── public/ # Static assets
└── package.jsonStart the dev server
cd my-docs
pnpm install # if you didn't auto-install
pnpm devYour site is now running at http://localhost:3000! 🎉
Write your first post
Create a new file in content/blog/ (or content/docs/ for documentation):
---
title: "My First Post"
description: "Starting my build in public journey"
author: "Your Name"
date: 2025-12-29
tags: ["Getting Started", "Build in Public"]
---
# My First Post
This is my first post using Fumadocs. Here's what I'm building...
## What I'm Working On
- Feature 1
- Feature 2
- Feature 3
```typescript
// Example code block with syntax highlighting
function hello() {
console.log("Hello, world!");
}
```
Add it to `content/blog/meta.json`:
```json title="content/blog/meta.json"
{
"title": "Blog",
"pages": [
"index",
"my-first-post"
]
}Using Fumadocs Components
The real power of Fumadocs comes from its built-in components. Here's what you can use:
Steps component
Perfect for tutorials and guides:
<Steps>
<Step>### First step Content for step 1</Step>
<Step>### Second step Content for step 2</Step>
</Steps>Tabs component
Show alternative approaches:
<Tabs items={['Option 1', 'Option 2']}>
<Tab value="Option 1">
Content for first option
</Tab>
<Tab value="Option 2">
Content for second option
</Tab>
</Tabs>Callouts
Highlight important information:
<Callout type="info">Useful information for readers</Callout>
<Callout type="warn">Warning - be careful here!</Callout>
<Callout type="tip">Pro tip for better results</Callout>Accordions
Collapsible content for FAQs:
<Accordions>
<Accordion title="How do I do X?">
Answer to the question
</Accordion>
<Accordion title="What about Y?">
Another answer
</Accordion>
</Accordions>Cards
Beautiful link cards:
<Cards>
<Card title="Documentation" description="Read the full docs" href="/docs" />
<Card
title="GitHub"
description="View the source code"
href="https://github.com/..."
/>
</Cards>Import Required
Remember to import components at the top of your MDX file: tsx import{" "} {(Step, Steps)} from 'fumadocs-ui/components/steps'; import {Callout} from 'fumadocs-ui/components/callout';
Deployment
Ready to ship? Use your preferred deployment platform. The tech stack for this blog is Tanstack Start + Cloudflare Workers to benefit from SSR and edge locations.
1. Configure Vite
Add the Cloudflare adapter to your vite.config.ts:
import { cloudflare } from "@cloudflare/vite-plugin";
import { tanstackStart } from "@tanstack/react-start/plugin/vite";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [
// ... other plugins
tanstackStart(),
cloudflare({ viteEnvironment: { name: "ssr" } }),
],
});2. Configure Wrangler
Create a wrangler.jsonc file for Cloudflare Workers configuration. Note the usage of nodejs_compat and the assets directory:
{
"$schema": "node_modules/wrangler/config-schema.json",
"name": "woltexai-blog",
"compatibility_date": "2025-12-01",
"compatibility_flags": ["nodejs_compat"],
"main": "@tanstack/react-start/server-entry",
"assets": {
"directory": "./dist/client",
"binding": "ASSETS"
}
}3. Setup CI/CD
Use GitHub Actions to deploy automatically. Here is the relevant part of the workflow:
- name: Deploy blog to Cloudflare Workers (production)
if: github.ref == 'refs/heads/main'
uses: cloudflare/wrangler-action@v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
workingDirectory: apps/blog
command: deployNow, every push to main will deploy the docs/blog to the edge! 🚀
Customization Tips
Add your branding
Edit app/layout.tsx to customize the site:
export default function RootLayout({ children }) {
return (
<html>
<body>
<DocsLayout
nav={{
title: "Your Blog Name",
// Add your logo
}}
links={[
{ text: "Home", url: "/" },
{ text: "Blog", url: "/blog" },
]}
>
{children}
</DocsLayout>
</body>
</html>
);
}Add analytics
Track your blog visitors with Rybbit (privacy-friendly, lightweight analytics):
For Tanstack Start / React Router:
export const Route = createRootRoute({
head: () => ({
scripts: [
{
src: import.meta.env.VITE_RYBBIT_TRACKING_URL,
defer: true,
"data-site-id": import.meta.env.VITE_RYBBIT_SITE_ID,
},
],
}),
component: RootComponent,
});For Next.js:
import Script from "next/script";
export default function RootLayout({ children }) {
return (
<html>
<body>
{children}
<Script
src={process.env.NEXT_PUBLIC_RYBBIT_TRACKING_URL}
data-site-id={process.env.NEXT_PUBLIC_RYBBIT_SITE_ID}
defer
/>
</body>
</html>
);
}Other Analytics Options
Rybbit is what Woltex uses, but Plausible, Fathom, and Umami are also great privacy-friendly alternatives.
Next Steps
Fumadocs Documentation
Official docs with everything you need to know
Example Sites
See Fumadocs in action on real projects
Component Library
Explore all available UI components
MDX Guide
Learn advanced MDX techniques
Woltex AI - Building in Public
Follow along as Woltex is built; a productivity platform focused on deliverables.
Production n8n: Queue Workers, Metrics & Monitoring
Running n8n in production isn't just spinning up a container. You need queue workers for reliability, metrics for visibility, and monitoring to catch issues before they become problems.
