Takumi
Integrate Takumi for framework agnostic and fast metadata image generation.
Installation
npm install takumi-jstakumi-js loads a native binding (@takumi-rs/core) on Node.js. Keep it out of the server bundle for Next.js by marking it external:
import type { NextConfig } from 'next';
const config: NextConfig = {
serverExternalPackages: ['@takumi-rs/core'],
};There are no actions needed for Vite-based frameworks.
Metadata Image
Generate metadata images dynamically with takumi-js.
Add the following under your loader, and define image metadata for pages:
export function getPageImage(page: (typeof source)['$inferPage']) {
const segments = [...page.slugs, 'image.webp'];
return {
segments,
url: `/og/docs/${segments.join('/')}`,
};
}import { notFound } from 'next/navigation';
import { source, getPageImage } from '@/lib/source';
import type { Metadata } from 'next';
export async function generateMetadata(props: PageProps<'/docs/[[...slug]]'>): Promise<Metadata> {
const params = await props.params;
const page = source.getPage(params.slug);
if (!page) notFound();
return {
title: page.data.title,
description: page.data.description,
openGraph: {
images: getPageImage(page).url,
},
};
}We append image.webp to the end of slugs so that we can access it via /og/docs/my-page/image.webp, which results in smaller image sizes. You could use image.png if you prefer.
Finally, create a route handler to generate images at build time:
import { getPageImage, source } from '@/lib/source';
import { notFound } from 'next/navigation';
import { ImageResponse } from 'takumi-js/response';
import { generate as DefaultImage } from 'fumadocs-ui/og/takumi';
export const revalidate = false;
export async function GET(_req: Request, { params }: RouteContext<'/og/docs/[...slug]'>) {
const { slug } = await params;
const page = source.getPage(slug.slice(0, -1));
if (!page) notFound();
return new ImageResponse(
<DefaultImage title={page.data.title} description={page.data.description} site="My App" />,
{
width: 1200,
height: 630,
format: 'webp',
},
);
}
export function generateStaticParams() {
return source.getPages().map((page) => ({
lang: page.locale,
slug: getPageImage(page).segments,
}));
}Takumi comes with pre-bundled full-axis (100-900) Geist and Geist Mono fonts, so you don't need to worry about it.
See Takumi's Documentation for more details or advanced usage.
Other Templates
There's other available templates, see Takumi Templates for a full list.
How is this guide?
Last updated on
