---
title: Access Control
description: 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()`](/docs/headless/source-api) 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.

```ts title="lib/source.ts"
import { docs } from '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.

```ts tab="lib/source.ts"
import { docs } from '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',
  });
}
```

```ts tab="References"
// [!code --]
import { source } from '@/lib/source';
// [!code ++:4]
import { createSource } from '@/lib/source';

const user = await getUser();
const source = createSource(user.permission);

const page = source.getPage(params.slugs);
```

### 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:

```tsx title="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.
