File System

File System

The File System utilities offer a way to organize and query file-system data in renoun. It is a powerful tool that allows you to define a schema for file exports and query those exports using a simple API.

To get started with the File System API, instantiate the Directory class to target a set of files and directories from the file system. You can then use the getEntry / getDirectory / getFile methods to query a specific descendant file or directory:

import { Directory } from 'renoun/file-system'

const posts = new Directory({
  path: 'posts',
  loader: {
    mdx: (path) => import(`./posts/${path}.mdx`),
  },
})

Here we are creating a new Directory instance that targets the posts directory relative to the working directory. We are also specifying a loader for the mdx file extension that will be used to load the file contents using the bundler.

Querying file system entries

import { Directory } from 'renoun/file-system'

const posts = new Directory({
  path: 'posts',
  loader: {
    mdx: (path) => import(`./posts/${path}.mdx`),
  },
})

export default async function Page({ slug }: { slug: string }) {
  const post = await posts.getFile(slug, 'mdx')
  const Content = await post.getExportValue('default')

  return <Content />
}

You can query the entries within the directory to help with generating navigations and index pages. For example, we can include only mdx file extensions to generate an index page of links to all posts using the getEntries method:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  include: '*.mdx',
  loader: {
    mdx: withSchema<PostType>((path) => import(`./posts/${path}.mdx`)),
  },
})

export default async function Page() {
  const allPosts = await posts.getEntries()

  return (
    <>
      <h1>Blog</h1>
      <ul>
        {allPosts.map(async (post) => {
          const pathname = post.getPathname()
          const frontmatter = await post.getExportValue('frontmatter')

          return (
            <li key={pathname}>
              <a href={pathname}>{frontmatter.title}</a>
            </li>
          )
        })}
      </ul>
    </>
  )
}

Type checking file exports

To improve type safety, you can utilize the withSchema helper to specify the schema for the file’s exports:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  loader: {
    mdx: withSchema<PostType>((path) => import(`./posts/${path}.mdx`)),
  },
})

Now when we call JavaScript#getExportValue and JavaScriptExport#getRuntimeValue we will have stronger type checking and autocomplete.

Schema Validation

You can also apply schema validation using libraries that follow the Standard Schema Spec like Zod, Valibot, or Arktype to ensure file exports conform to a specific schema:

import { Directory, withSchema } from 'renoun/file-system'
import { z } from 'zod'

const posts = new Directory({
  path: 'posts',
  loader: {
    mdx: withSchema(
      {
        frontmatter: z.object({
          title: z.string(),
          date: z.date(),
        }),
      },
      (path) => import(`./posts/${path}.mdx`)
    ),
  },
})

Alternatively, you can define a schema yourself using both TypeScript types and custom validation functions:

import { Directory, withSchema } from 'renoun/file-system'

interface PostType {
  frontmatter: {
    title: string
    date: Date
  }
}

const posts = new Directory({
  path: 'posts',
  loader: {
    mdx: withSchema<PostType>(
      {
        frontmatter: (value) => {
          if (typeof value.title !== 'string') {
            throw new Error('Title is required')
          }

          if (!(value.date instanceof Date)) {
            throw new Error('Date is required')
          }

          return value
        },
      },
      (path) => import(`./posts/${path}.mdx`)
    ),
  },
})

The file system utilities are not limited to MDX files and can be used with any file type. By organizing content and source code into structured collections, you can easily generate static pages and manage complex routing and navigations.

API Reference