Skip to main content

Use cases

Headless + custom frontend

When you outgrow the default docs shell — or want docs rendered inside your product UI, in a mobile app, or on a marketing site — point at NookDocs's APIs and render yourself. The architectural trade-offs and when NOT to do this.

NookDocs renders your MDX with a built-in shell — sidebar, header, search modal, theme system, footer. For 95% of tenants this is the right default. The other 5% need something the default shell doesn't provide: docs embedded in the product UI, docs rendered via a mobile app, docs as part of a larger marketing site with custom chrome, docs that feed into a third-party helpdesk tool.

This guide covers the headless pattern — fetching your docs via API, rendering with your own UI — when it makes sense, and when to push back against it.

When to go headless

In-product docs panel

You want docs to appear inside your product's dashboard as a slide-out panel, with your own chrome. Users never leave the product to get help.

Mobile app docs

Your product has a native iOS / Android app. Users need in-app documentation that matches the app's visual identity, not a web-shell-in-a-WebView.

Marketing-site integration

Docs are one section of a larger corporate site with its own nav + footer. You want the MDX content, not the docs shell.

Customised search / chat UX

You want to build a domain-specific chat interface (e.g. an AI assistant that blends docs + your internal KB + real-time product state).

When to NOT go headless

You want a different theme

NookDocs ships 10 themes + full token-level customisation. Try theme before rebuilding from scratch.

You dislike the default header

Replace via config — logo, links, CTA, banner all tenant-configurable. See site settings.

You want your brand colour

colors.primary / colors.light / colors.dark cover it.

You want to add a GA pixel

integrations.ga4 exists. 16 analytics providers ship. See integrations overview.

Most "I need custom docs" asks turn out to be "I haven't explored the config". Exhaust config options before committing to headless work — the maintenance cost is real.

The trade-offs

Going headless shifts responsibility:

ConcernDefault shellHeadless
Rendering the MDXNookDocsYou — use @mdx-js/react or similar
Component libraryNookDocs ships 47 componentsYou implement every component you use
SearchBuilt-in ⌘KYou build or integrate
Theme tokensAutomatic light/darkYou manage
SEO meta + sitemapAuto-emittedYou emit
/llms.txt + RSSAuto-generatedOptional — you integrate if needed
Versioning + multi-languageBuilt-inYou implement
Analytics beaconAutoYour tool chain

You're not losing the content-management story (still author MDX, still edit in the dashboard, still auto-sync from Git) — you're losing the rendering-to-production pipeline. That's more work than it sounds.

The API surface for headless

NookDocs exposes JSON + Markdown endpoints for programmatic consumption:

Page content

# Raw MDX for one page (agent-filtered)
GET https://{tenant}.nookdocs.site/<page-path>.md

# Same page, JSON envelope with frontmatter + source
GET https://api.nookdocs.com/v1/projects/{projectId}/pages/<page-path>

Search

POST https://api.nookdocs.com/v1/projects/{projectId}/pages/search
Content-Type: application/json

{
  "query": "custom domain",
  "limit": 10
}

Returns ranked results with snippet highlights. See API reference for the full shape.

Navigation

# The parsed navigation tree (tabs + groups + pages)
GET https://api.nookdocs.com/v1/projects/{projectId}/config

Returns the nookdocs.config.json navigation as structured JSON. Your frontend renders a sidebar from this.

AI assistant

POST https://{tenant}.nookdocs.site/api/v1/projects/{projectId}/assistant/ask
Content-Type: application/json

{
  "query": "How do I set up webhooks?",
  "history": [...]
}

Returns a streamed SSE response. Your frontend subscribes + renders incrementally. See AI assistant.

llms.txt + sitemap

Still auto-generated at the tenant URL. Useful even in headless mode for LLM crawlers that expect the standard endpoints:

GET https://{tenant}.nookdocs.site/llms.txt
GET https://{tenant}.nookdocs.site/llms-full.txt
GET https://{tenant}.nookdocs.site/sitemap.xml

A minimal headless React example

// DocsPanel.tsx — in-product slide-out docs
import { useState, useEffect } from "react";
import { MDXRemote } from "next-mdx-remote/rsc";
import { serialize } from "next-mdx-remote/serialize";

async function fetchDoc(path: string) {
  const res = await fetch(
    `https://api.nookdocs.com/v1/projects/${PROJECT_ID}/pages/${path}`,
    { headers: { Authorization: `Bearer ${API_KEY}` } },
  );
  const json = await res.json();
  return await serialize(json.source_mdx, { parseFrontmatter: true });
}

export function DocsPanel({ path }: { path: string }) {
  const [content, setContent] = useState<any>(null);

  useEffect(() => {
    fetchDoc(path).then(setContent);
  }, [path]);

  if (!content) return <div>Loading...</div>;

  return (
    <aside className="docs-panel">
      <MDXRemote
        source={content}
        components={{
          // Your component implementations
          Callout: YourCallout,
          Card: YourCard,
          ParamField: YourParamField,
          // ...47 more components to implement
        }}
      />
    </aside>
  );
}

The components prop is where the real work lives — you're implementing every NookDocs component in your own design system. Ship 1 at a time as pages need them.

Component strategy

Three approaches, increasing effort:

1. Minimal set (ship fast)

Implement only the ~15 most-used components: <Callout>, <Note>, <Warning>, <Info>, <Card>, <CardGroup>, <Steps>, <Step>, <Tabs>, <CodeGroup>, <ParamField>, <ResponseField>, <Frame>, <Accordion>.

Unknown components render as raw children (ugly but functional). Good for shipping an MVP.

2. Full set (parity)

Implement all 47 components to match NookDocs's built-in library. Expensive — 2-4 weeks of component work. Pays off if docs are central to your product.

3. Subset + redirect

Implement 15-20 core components. For pages that use fancy components (like <ColorPalette> or <Mermaid>), render a "View this page in full" link that opens the NookDocs-hosted version.

Most teams land at approach 2 or 3.

SSR vs client-only

Headless docs work either as part of an SSR app (Next.js, Remix, Astro) OR a fully-client-rendered SPA:

  • SSR — fetch at build time or per-request, serialize MDX, hydrate. Better SEO, faster first paint. Preferred for public-facing headless docs.

  • Client-only — fetch on mount, serialize + render in the browser. Worse SEO (search engines see empty shell), faster iteration. OK for in-product panels where SEO doesn't matter.

For in-product docs (private, authenticated), client-only is usually fine — your users authenticate before seeing docs, so Google can't crawl anyway.

Performance considerations

  • Don't fetch MDX on every render — cache aggressively. Most docs pages don't change minute-to-minute.

  • Lazy-load heavy components<Mermaid> needs Mermaid.js; <CodeGroup> needs Prism. Lazy-load to keep initial bundle lean.

  • Stream where possible — the assistant endpoint streams SSE; render tokens as they arrive, don't wait for completion.

  • Prefetch likely next pages — docs readers tend to follow the "Next" footer link; prefetch on hover.

When to reconsider

At some point, implementing + maintaining a headless docs pipeline becomes more work than learning to customise the default shell. Signs you should reconsider:

  • You've built 20+ components and are still hitting edge cases.

  • Every shipping change needs a coordinated update across MDX + your frontend + your search index.

  • Users ask for features the default shell has (search modal, copy-for-LLMs, RSS, etc.).

Migration back to the default shell is usually straightforward — your MDX is portable, your config translates, your search index is already in NookDocs. The hard part is sunsetting your custom work without disruption.

Hybrid pattern — partial headless

Compromise pattern: use the NookDocs default shell for docs.acme.com + embed specific pages in-product via the API:

docs.acme.com            — full default shell (public docs)
app.acme.com/help-panel  — headless in-product docs (private)

Authoring + editing happens in one place. Rendering branches by audience. Less work than full headless; still covers the in-product use case.

Common headless pitfalls

Related

Was this page helpful?

Last updated August 11, 2026

Headless + custom frontend | NookDocs | NookDocs