Fumadocs

Access Control

Limit the access of content.

Overview

Thanks to the flexible design, it is simple to implement access control with Fumadocs.

Loader API

If you are using loader() API, you can create the loaders on-demand, and filter files from content source to restrict accessible content.

For example, this setup with Fumadocs MDX allows only MDX files with frontmatter permission: public to be shown.

lib/source.ts
import { docs } from 'fumadocs-mdx:collections/server';
import { loader, update } from 'fumadocs-core/source';

const filteredSource = update(docs.toFumadocsSource())
  .files((files) =>
    files.filter((file) => {
      // keep meta files (e.g. `meta.json`)
      if (file.type === 'meta') return true;

      // access file data (type-safe)
      return file.data.permission === 'public';
    }),
  )
  .build();

export const source = loader(filteredSource, {
  baseUrl: '/docs',
});

Or with more complicated setup, you may turn source into a factory function.

import { docs } from 'fumadocs-mdx:collections/server';
import { loader, update } from 'fumadocs-core/source';

// uncached, it's better to cache it in-memory for real use case.
export function createSource(permission: 'public' | 'admin') {
  const filteredSource = update(docs.toFumadocsSource())
    .files((files) =>
      files.filter((file) => {
        if (file.type === 'meta') return true;

        return file.data.permission === permission;
      }),
    )
    .build();

  return loader(filteredSource, {
    baseUrl: '/docs',
  });
}

Advantages

What is special with this approach is that the access is limited at input level, rather than at rendering phase or build-time.

This guarantees the protected content is filtered entirely, and without compromise on flexibility.

Custom Implementation

Without using Loader API, you can also implement your access control at framework-level (e.g. via Next.js routing & proxy).

For example, to limit access to certain pages:

page.tsx
import { notFound } from 'next/navigation';
import { source } from '@/lib/source';

export default async function Page({ params }) {
  const { slugs } = await params;
  const user = await getUser();
  const page = source.getPage(slugs);

  if (!page) notFound();
  if (page.data.permission !== user.permission) notFound();

  // render page...
}

Note that it is up to you to manage the page tree (sidebar items), search and other details.

This approach offers the best flexibility, but it doesn't rely on Fumadocs itself for access control, the setup may take longer and be more complicated.

It is better to consult the docs of your React.js framework when implementing.

How is this guide?

Last updated on

On this page