Configuration
Put plugin options and optional Vite settings in sitelo.config.js. No vite.config.js is required.
export default {
site: 'https://example.com',
rss: {
site: 'https://example.com',
title: 'My Blog',
description: 'Latest posts',
routePrefix: '/blog',
},
vite: {
publicDir: 'static',
build: {
emptyOutDir: true,
outDir: 'public',
},
server: {
port: 8888,
},
},
}Plugin options
pagesDir— default'src'pageExtensions— which suffixes count as pagescleanUrls— defaulttrue(/about/index.html)site— base URL; enablessitemap.xmlrss— RSS feed configpagefind—trueor options object; indexes the site with Pagefind aftersitelo buildmissingAssets—'error'or'warn'generatedTypesDir— default'.sitelo/types'renderConcurrency/renderBatchSize— build parallelismdebug— verbose loggingdevToolbar— defaulttrue; setfalseto hide the dev-only toolbar (route, source file, params, island count)devToolbarDocsUrl— Docs link in the toolbar (defaulthttps://sitelo.js.org/docs)
Dev toolbar
While sitelo (dev) is running, a small bar at the bottom of each page shows the current route, page file, params, and how many server islands are on the page. Use Copy for a debug blob when filing issues. It never appears in sitelo build output.
// sitelo.config.js
export default {
devToolbar: false, // hide for everyone on this project
}Vite options
Anything under vite is merged into Vite’s config. CLI flags (e.g. --port) override both.
Existing vite.config.js
Still supported — either Vite-only options, or full plugin control:
// Vite options only; sitelo still injects the plugin
export default {
publicDir: 'static',
server: { port: 8888 },
}// Register the plugin yourself
import htmlPages from 'sitelo'
export default {
plugins: [htmlPages({
site: 'https://example.com',
})],
}If the plugin is already in your Vite config, put plugin options there — not also as plugin options in sitelo.config.js (sitelo will error).
Sitemap & RSS
Set site to emit dist/sitemap.xml.
RSS:
export default {
rss: {
site: 'https://example.com',
title: 'My Blog',
description: 'Latest posts',
routePrefix: '/blog',
},
}Produces dist/rss.xml with an item for every page under routePrefix.
Pagefind search
Opt-in static search powered by Pagefind. Enable it, mark the content to index, mount the UI, then sitelo build.
1. Enable indexing
Set pagefind: true. After sitelo build you get dist/pagefind/, and by default a copy in public/pagefind/ so the next sitelo (dev) or sitelo preview can serve /pagefind/ without rebuilding.
export default {
pagefind: true,
}2. Mark content and add a mount point
Put data-pagefind-body on the main content so nav/footer aren’t indexed. Leave an empty element for the search UI:
export default () => `
<html lang="en">
<head>
<title>My site</title>
<link rel="stylesheet" href="/styles.css">
<script type="module" src="/main.js"></script>
</head>
<body>
<header>
<a href="/">Home</a>
<div id="search"></div>
</header>
<main data-pagefind-body>
<h1>Hello</h1>
<p>Only this region is indexed.</p>
</main>
</body>
</html>
`import {
html, head, title, link, script, body, header, a, div, main, h1, p,
} from 'javascript-to-html'
export default () =>
html({ lang: 'en' },
head(
title('My site'),
link({ rel: 'stylesheet', href: '/styles.css' }),
script({ type: 'module', src: '/main.js' }),
),
body(
header(
a({ href: '/' }, 'Home'),
div({ id: 'search' }),
),
main({ 'data-pagefind-body': '' },
h1('Hello'),
p('Only this region is indexed.'),
),
),
)export default function Home() {
return (
<html lang="en">
<head>
<title>My site</title>
<link rel="stylesheet" href="/styles.css" />
<script type="module" src="/main.js" />
</head>
<body>
<header>
<a href="/">Home</a>
<div id="search" />
</header>
<main data-pagefind-body="">
<h1>Hello</h1>
<p>Only this region is indexed.</p>
</main>
</body>
</html>
)
}3. Mount the Pagefind UI
Load /pagefind/pagefind-ui.js and pagefind-ui.css from your client script (only after a build has produced the index):
async function initSearch() {
const mount = document.querySelector('#search')
if (!mount) return
// Index only exists after `sitelo build` (synced to public/pagefind by default)
try {
const probe = await fetch('/pagefind/pagefind-ui.js', { method: 'HEAD' })
if (!probe.ok) return
} catch {
return
}
const style = document.createElement('link')
style.rel = 'stylesheet'
style.href = '/pagefind/pagefind-ui.css'
document.head.appendChild(style)
await new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = '/pagefind/pagefind-ui.js'
script.onload = resolve
script.onerror = reject
document.body.appendChild(script)
})
new window.PagefindUI({
element: '#search',
showImages: false,
})
}
initSearch()4. Build and ignore the synced bundle
sitelo build
# then: sitelo preview — or sitelo (dev) using public/pagefindpublic/pagefind/Advanced options (syncPublic, glob, language, selectors, …) go on an object: pagefind: { syncPublic: false, glob: '**/*.html' }. Full UI options: pagefind.app.
404
Create src/404.ht.js for dist/404.html. Otherwise a clean default is generated.