---
title: Loader API
description: Turn content sources into a unified interface
---

## What it does?

`loader()` provides an interface for Fumadocs to integrate with different content sources.

- Generate [page slugs and page tree](/docs/headless/page-conventions).
- Assign URL to each page.
- Output useful utilities to interact with content.

<Callout type='warn' title="Important to Know">

- `loader()` is a server-side API, not a build-time magic or browser compatible API.
- It uses in-memory storage, the files are passed from your content sources.

</Callout>

## Usage

You can use it with content sources like Fumadocs MDX, see [`source`](/docs/headless/source-api/source) for details.

```ts
import { loader } from 'fumadocs-core/source';
import { docs } from 'collections/server';

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

### URL

You can override the base URL, or specify a function to generate URL for each page.

```ts
import { loader } from 'fumadocs-core/source';

loader({
  baseUrl: '/docs',
  // or you can customize it with function
  url(slugs, locale) {
    if (locale) return '/' + [locale, 'docs', ...slugs].join('/');
    return '/' + ['docs', ...slugs].join('/');
  },
});
```

### Slugs

Define a custom function to generate slugs from page files.

```ts
import { loader } from 'fumadocs-core/source';

loader({
  slugs(file) {
    console.log(file.path, file.data);
    // or return `undefined` to fallback to default value
    return ['my', 'slug'];
  },
});
```

### Icons

Load the [icon](/docs/headless/page-conventions#icons) property specified by page and meta files.

```ts
import { loader } from 'fumadocs-core/source';
import { icons } from 'lucide-react';
import { createElement } from 'react';

loader({
  icon(icon) {
    if (!icon) {
      // You may set a default icon
      return;
    }

    if (icon in icons) return createElement(icons[icon as keyof typeof icons]);
  },
});
```

### I18n

Pass the `i18n` config to loader.

```ts title="lib/source.ts"
import { i18n } from '@/lib/i18n';
import { loader } from 'fumadocs-core/source';

export const source = loader({
  i18n, // [!code highlight]
});
```

With i18n enabled, loader will generate a page tree for every locale.

If translations are missing for a page, it fallbacks to [`fallbackLanguage`](/docs/headless/internationalization/config#fallback-language).

## Output

It outputs a content loader instance.

### Get Page

Get page with slugs.

```ts
import { source } from '@/lib/source';

source.getPage(['slug', 'of', 'page']);

// with i18n
source.getPage(['slug', 'of', 'page'], 'locale');
```

### Get Pages

Get a list of page available for locale.

```ts
import { source } from '@/lib/source';

// from any locale
source.getPages();

// for a specific locale
source.getPages('locale');
```

### Page Tree

```ts
import { source } from '@/lib/source';

// without i18n
source.getPageTree();

// with i18n
source.getPageTree('locale');
```

### Get from Node

The page tree nodes contain references to their original file path.
You can find their original page or meta file from the tree nodes.

```ts
import { source } from '@/lib/source';

source.getNodePage(pageNode);
source.getNodeMeta(folderNode);
```

### Params

A function to generate output for Next.js `generateStaticParams`.
The generated parameter names will be `slug: string[]` and `lang: string` (i18n only).

```ts title="app/[[...slug]]/page.tsx"
import { source } from '@/lib/source';

export function generateStaticParams() {
  return source.generateParams();
}
```

### Language Entries

Get available languages and its pages.

```ts
import { source } from '@/lib/source';

// language -> pages
const entries = source.getLanguages();
```

## Client API

Loader API is best when used in RSC environments, which allows passing JSX nodes across the server-client boundary.

For non-RSC environments, it provides a tiny serialization layer.

```ts tab="Server"
import { source } from '@/lib/source';

// where you pass loader data
async function loader() {
  const pageTree = source.getPageTree();

  return {
    pageTree: await source.serializePageTree(pageTree),
  };
}
```

```tsx tab="Client"
import { useFumadocsLoader } from 'fumadocs-core/source/client';

function MyComponent() {
  // `useLoaderData()` receives loader data (provided by your React framework)
  const { pageTree, ...data } = useFumadocsLoader(useLoaderData());

  // now render it (e.g. via Fumadocs UI)
  return <DocsLayout tree={pageTree}>...</DocsLayout>;
}
```

## Dynamic Loader

`dynamicLoader()` provides extra revalidation functionality to content sources, you should use it with a [dynamic source](/docs/headless/source-api/source#dynamic-source).

```ts
import { dynamicLoader } from 'fumadocs-core/source/dynamic';
import { createMySource } from './my-source';

const source = dynamicLoader(createMySource(), {
  baseUrl: '/docs',
});

export async function getSource() {
  return source.get();
}
```

When you call `await source.get()`, it returns a normal content loader object:

```ts
const docs = await getSource();

const page = docs.getPage(['...']);
```

### Revalidation

Dynamic sources are cached by the loader.

If your source changes over time, call `invalidate()` to clear the cache, or `revalidate()` to update the cache:

```ts
source.invalidate();

await source.revalidate();
```

For [multi-source](/docs/headless/source-api/source#multiple-sources) setups, you can invalidate/revalidate a specific source:

```ts
source.invalidate('docs');

await source.revalidate('docs');
```
