Fuma Content

Introduction

The primitive for content processing.

What is This?

Fuma Content introduces primitives for content processing, with a carefully designed abstraction to make it highly extensible and flexible.

The Idea

Fuma Content process content via Collections and Plugins.

  • Collections implement a list of handlers, each handler contains options for specific functionality, such as MDX compilation.
  • Plugins declare & read options from collection handlers, then integrate into bundler or file generation layer to process content.

Built-in Collections: MDX, Meta (supporting JSON and YAML files).

Fuma Content is a foundational layer, we plan to build more on top such as WYSIWYG editor plugin, or CMS plugin.

Requirements

Fuma Content requires a bundler or runtime loader (like Bun or Node.js loader) to work, it supports major frameworks like Vite, Next.js, and JavaScript runtimes.

Installation

npm i fuma-content

Configure it according to your framework.

next.config.mjs
import { createContent } from "fuma-content/next";

/** @type {import('next').NextConfig} */
const config = {
  reactStrictMode: true,
};

const withContent = await createContent();

export default withContent(config);

Create a config file, you can define collections and plugins:

content.config.ts
import { defineConfig } from "fuma-content/config";
import { defineMDX } from "fuma-content/collections/mdx";

export default defineConfig({
  collections: {
    docs: defineMDX({
      dir: "content/docs",
      // you can define a Standard Schema for frontmatter
      // frontmatter: ...
    }),
  },
});

Fuma Content is now configured, start the dev server of your framework (e.g. next dev) to access the compiled content.

Usage

Each collection generates its own file in the .content folder, it's recommended to add a path alias:

tsconfig.json
{
  "compilerOptions": {
    "paths": {
      "content/*": [".content/*"]
    }
  }
}

For built-in collections, you can access them like:

import { docs } from "content/mdx";

for (const file of docs.list()) {
  const id = file.id;
  // compiled properties & typed frontmatter
  const { frontmatter, ...rest } = file.compiled;
}

function Page({ id }: { id: string }) {
  const file = docs.get(id);
  if (!file) return;

  // default refers to the content body renderer
  const { default: MDX } = file.compiled;

  return (
    <div className="prose">
      <MDX
        // MDX Components
        components={{
          h1: (props) => <h1 {...props} className="text-4xl" />,
        }}
      />
    </div>
  );
}