Fumadocs

Changelog

Show the releases of your project.

Installation

Install the timeline component, it adds @fumadocs/changelog to your dependencies:

npx @fumadocs/cli@latest add changelog

Usage

Create a page under your Home Layout. Fetch a page of entries, render them with ChangelogTimeline, and link to the other pages with ChangelogPagination:

app/(home)/changelog/[[...page]]/page.tsx
import { notFound } from 'next/navigation';
import { githubReleases } from '@fumadocs/changelog/github';
import { ChangelogPagination, ChangelogTimeline } from '@/components/changelog';

export const revalidate = 3600;

export default async function Page(props: PageProps<'/changelog/[[...page]]'>) {
  const { page: segments = [] } = await props.params;
  const page = Number(segments[0] ?? 1);
  const { entries, nextPage } = await githubReleases({
    owner: 'fuma-nama',
    repo: 'fumadocs',
    token: process.env.GITHUB_TOKEN,
    page,
  });
  if (entries.length === 0) notFound();

  return (
    <div className="mx-auto flex w-full max-w-[900px] flex-col gap-10 px-4 py-12">
      <ChangelogTimeline entries={entries} />
      <ChangelogPagination
        newer={page > 1 ? `/changelog/${page - 1}` : undefined}
        older={nextPage ? `/changelog/${nextPage}` : undefined}
      />
    </div>
  );
}

On Astro, the pages are generated at build time. The timeline is a child of the island, it renders on the server only, without interactivity like copy buttons.

Sources

GitHub Releases

githubReleases() returns a page of the published releases of a repository, with nextPage when there are older releases.

  • Versions are read from the release names like fumadocs-ui@16.17.0, or their tags.
  • Entries have a prerelease property for pre-releases.
  • GitHub limits unauthenticated requests to 60 per hour, pass a token and cache the page, like export const revalidate = 3600 on Next.js.
  • GitHub lists the first 1000 releases, or 10000 with a token, later pages have no entries.

Prop

Type

Custom Entries

Entries are plain objects, create them from any source:

Prop

Type

For example, a collection of MDX files. Pass body to render the content yourself:

app/(home)/changelog/page.tsx
import { changelogSource } from '@/lib/source';
import { getMDXComponents } from '@/components/mdx';
import { ChangelogTimeline, type ChangelogTimelineEntry } from '@/components/changelog';

export default function Page() {
  const entries: ChangelogTimelineEntry[] = changelogSource.getPages().map((page) => {
    const MDX = page.data.body;

    return {
      id: page.url,
      title: page.data.title,
      date: new Date(page.data.date),
      body: <MDX components={getMDXComponents()} />,
    };
  });

  return (
    <div className="mx-auto w-full max-w-[900px] px-4 py-12">
      <ChangelogTimeline entries={entries} />
    </div>
  );
}

How is this guide?

Last updated on