Contentlayer is a content SDK that validates and transforms your content into type-safe JSON data you can easily import into your application.
The capri.build website uses Contentlayer to read local MDX files. Take a look at the source code on GitHub to dive into the details.
In package.json
the contentlayer CLI is used to transform the MDX files:
"scripts": {
"dev": "contentlayer dev & vite dev",
"build": "contentlayer build && vite build && vite build --ssr",
"serve": "vite preview"
}
The page component that renders the document you are currently viewing looks similar to this:
import { useDoc } from "./useDoc";
import { useMdx } from "./useMdx";
import { Card } from "./components/Card";
import { Note } from "./components/Note";
import { Video } from "./components/Video";
const mdxComponents = {
Card,
Note,
Video,
};
type Props = {
url?: string; // injected by preact-router
};
export function Doc({ url }: Props) {
const doc = useDoc(url);
const Mdx = useMdx(doc.body.code);
return (
<div>
<h1>{doc.title}</h1>
<Mdx components={mdxComponents} />
</div>
);
}
The content is loaded via the useDoc()
hook. This is what it looks like:
import { allDocs } from "../.contentlayer/generated";
export function useDoc(url?: string) {
const doc = allDocs.find((doc) => doc.url === url);
if (!doc) throw new Error(`No doc found for ${url}`);
return doc;
}
Finally the useMdx()
hook is used to render the content. It looks like this:
import { useMemo } from "preact/hooks";
import * as _jsx_runtime from "preact/jsx-runtime";
export function useMdx(code: string) {
return useMemo(() => {
const scope = { _jsx_runtime };
const fn = new Function(...Object.keys(scope), code);
const { default: Component } = fn(...Object.values(scope));
return Component;
}, [code]);
}