Before you scaffold anything, take a look at the live demo — sveltepress.github.io/sveltepress/blog-demo. Everything described on this page is already working there.
Source: packages/example-blog in the monorepo. After cloning the repository, run pnpm install and pnpm --filter @sveltepress/example-blog dev from the repository root. The demo starts on http://localhost:36739.
@sveltepress/theme-blog is a magazine-style blog theme with a left-rail sidebar, masonry post grid, per-post OG images, RSS, Pagefind search, and Giscus comments. This page walks you through scaffolding a working blog.
Install
npm install --save @sveltepress/theme-blog yarn add @sveltepress/theme-blog pnpm install @sveltepress/theme-blog bun add @sveltepress/theme-blog The theme is loaded by the Sveltepress Vite plugin:
npm install --save @sveltepress/vite yarn add @sveltepress/vite pnpm install @sveltepress/vite bun add @sveltepress/vite The theme requires @sveltejs/adapter-static because it generates a fully static site (prerendered HTML, JSON, RSS, OG images).
npm install --save @sveltejs/adapter-static yarn add @sveltejs/adapter-static pnpm install @sveltejs/adapter-static bun add @sveltejs/adapter-static Pagefind builds the local search index after Vite finishes:
npm install --save pagefind yarn add pagefind pnpm install pagefind bun add pagefind Configure Vite
import { import blogThemeblogTheme } from '@sveltepress/theme-blog'
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig } from 'vite'
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
plugins: [
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({
SveltepressVitePluginOptions.theme?: ResolvedTheme | undefinedtheme: import blogThemeblogTheme({
title: stringtitle: 'My Blog',
description: stringdescription: 'Thoughts on Svelte and the web.',
base: stringbase: 'https://example.com',
author: {
name: string;
avatar: string;
bio: string;
socials: {
github: string;
twitter: string;
rss: string;
};
}
author: {
name: stringname: 'Your Name',
avatar: stringavatar: '/avatar.png',
bio: stringbio: 'Short bio shown in the sidebar.',
socials: {
github: string;
twitter: string;
rss: string;
}
socials: {
github: stringgithub: 'your-handle',
twitter: stringtwitter: 'your-handle',
rss: stringrss: '/rss.xml',
},
},
navbar: {
title: string;
to: string;
}[]
navbar: [
{ title: stringtitle: 'Home', to: stringto: '/' },
{ title: stringtitle: 'Timeline', to: stringto: '/timeline/' },
{ title: stringtitle: 'Tags', to: stringto: '/tags/' },
],
}),
}),
],
}) Configure SvelteKit
import adapter from '@sveltejs/adapter-static'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
export default {
extensions: ['.svelte'],
preprocess: [vitePreprocess()],
kit: {
adapter: adapter({
pages: 'dist',
assets: 'dist',
fallback: '404.html',
}),
prerender: {
handleMissingId: 'ignore',
handleUnseenRoutes: 'ignore',
},
paths: {
base: process.env.BASE_PATH ?? '',
relative: false,
},
},
compilerOptions: {
runes: true,
},
} BASE_PATH lets you deploy under a subpath (e.g. GitHub Pages project site). Leave the env var unset for root deployments.
Write your first post
Create src/posts/hello-world.md:
---
title: Hello world
date: 2026-04-17
tags: [intro]
category: meta
excerpt: First post on the new blog.
---
# Hello
Welcome to my blog. Everything is markdown. Routes auto-scaffolded
On the next vite dev or vite build, the theme writes these files if they don't exist. Edit them freely — the scaffolder only creates missing files.
| Path | Purpose |
|---|---|
src/routes/+layout.ts | Enables prerender + trailingSlash: 'always' |
src/routes/+layout.svelte | Wraps pages in GlobalLayout |
src/routes/+page.{server.ts,svelte} | Paginated home |
src/routes/page/[n]/... | Page 2+ of the list |
src/routes/posts/[slug]/... | Individual post pages |
src/routes/tags/+page.svelte | Tag index |
src/routes/tags/[tag]/... | Posts for a tag |
src/routes/categories/[cat]/... | Posts for a category |
src/routes/timeline/+page.svelte | Archive timeline |
Build
Add Pagefind to your project build script:
{
"scripts": {
"build": "vite build && pagefind --site dist"
}
} pnpm build The Pagefind step indexes the built site so the built-in search modal (⌘K / Ctrl+K) works. The current theme always renders this Pagefind integration, so keep the post-build indexing step enabled.
The resulting dist/ is a static bundle deployable to any static host.