Writing a theme
A theme is an Astro project using the
@gophenberg/astro kit. You write two files: an Astro config
naming the integration, and a theme definition naming your
layouts. The kit does the rest, including the routes.
The best starting point is copying the reference theme from the Gophenberg repository. This page walks its shape.
The two files
Section titled “The two files”astro.config.mjs turns the project into a theme:
import { gophenberg } from '@gophenberg/astro/config'import { defineConfig } from 'astro/config'
export default defineConfig({ integrations: [gophenberg()],})src/theme.ts declares what your theme provides:
import { defineTheme } from '@gophenberg/astro'
import Archive from './layouts/Archive.astro'import Base from './layouts/Base.astro'import NotFound from './layouts/NotFound.astro'import Post from './layouts/Post.astro'
export default defineTheme({ layouts: { Base, Post, Archive, NotFound }, pagination: { perPage: 10 }, seo: { siteName: 'My Site' },})NotFound is optional, and a blocks map can join the object to
override single block types, covered in
rendering blocks.
The routes come from the kit
Section titled “The routes come from the kit”You write no pages for posts. The integration injects the front
page, /{type}/page/{n} listings, /{type}/{slug} posts, the 404
page, and the health route Gophenberg probes at startup. Each page
route renders one of your layouts with the content already
fetched.
Your own pages coexist with them: src/pages/about.astro serves
/about as in any Astro site. To put posts on a page of your own,
read the content API directly.
The layouts
Section titled “The layouts”Base wraps every page. It receives seo and an optional
title, and its one obligation is rendering GophenbergHead
inside <head>, which emits the page title and the stylesheets
that make block content look right:
---import { GophenbergHead } from '@gophenberg/astro/components'
interface Props { seo: { siteName: string } title?: string}
const { seo, title } = Astro.props---
<html lang="en"> <head> <GophenbergHead title={title ? `${title} | ${seo.siteName}` : seo.siteName} /> </head> <body> <header><a href="/">{seo.siteName}</a></header> <main><slot /></main> </body></html>Post receives the post, your blocks overrides, and seo,
and renders the content through the Blocks component:
---import { Blocks } from '@gophenberg/astro/components'import type { BlockComponentMap, Post } from '@gophenberg/astro'import Base from './Base.astro'
interface Props { post: Post blocks?: BlockComponentMap seo: { siteName: string }}
const { post, blocks, seo } = Astro.props---
<Base seo={seo} title={post.title}> <article> <h1>{post.title}</h1> <Blocks html={post.content} components={blocks} post={post} /> </article></Base>Archive receives posts (summaries without content), total,
page, perPage, type, and seo. NotFound receives seo.
Trying it
Section titled “Trying it”astro dev runs your theme against a running Gophenberg. It needs
two addresses, one for content and one for the block stylesheets,
which the dev server does not serve itself:
GOPHENBERG_API_URL=http://localhost:8081 \GOPHENBERG_ASSET_ORIGIN=http://localhost:8081 \pnpm astro devWhen it looks right, installing a theme takes it to production.