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.
The navbar Blog demo is that finished showcase. The navbar Playground opens the Feature directory. Configuration, Writing posts, Features, and Customisation are the Blog theme Entries that auto-boot the Blog starter. This getting-started page is not a Playground Entry and has no Open in Playground control.
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.