Swifty believes in sensible defaults, but when you want to tweak things, the config file is your friend. Create a config.yaml (or config.yml or config.json if that's your jam) in your project root.
The Full Config Menu
Here's everything you can configure, with the defaults shown:
sitename: Swifty
author: Taylor Swift
site_url: https://yoursite.com
base_path: ""
breadcrumb_separator: "»"
breadcrumb_class: swifty_breadcrumb
link_class: swifty_link
tag_class: tag
prev_next_class: swifty_link
default_layout_name: default
default_link_name: links
max_image_width: 800
image_quality: 80
responsive_image_widths: [320, 640, 800]
responsive_image_sizes: 100vw
default_og_image: /images/share.png
highlight_theme: monokai-sublime
minify: true
minify_html: true
minify_css: true
minify_js: true
morphing: true
prefetching: true
morph_target: main
search: true
search_content_limit: 5000
search_results_limit: 10
summary_length: 200
related_pages_limit: 3
words_per_minute: 200
build_concurrency: 16
server_port: 3000
livereload_port: 35729
watcher_delay: 100
watcher_interval: 500
watcher_use_polling: false
navigation_cache_size: 20
navigation_cache_ttl: 15
# Pagination (disabled until page_count is set)
# page_count: 10
pagination_class: swifty_pagination
pagination_link_class: swifty_pagination_link
pagination_current_class: swifty_pagination_current
# Sorting
date_sort_order: desc
date_locale: en-GB
timezone: UTC
dateFormat:
weekday: short
month: short
day: numeric
year: numeric
What Does What?
| Option | What it does |
|---|---|
sitename | Your site's name - use <%= sitename %> anywhere to display it |
author | Your name (or whoever's taking credit) |
site_url | Full URL of your site; required for RSS and relative social images |
base_path | URL prefix such as /project when the site is hosted below an origin path |
breadcrumb_separator | The character between breadcrumb links |
breadcrumb_class | CSS class for breadcrumb links |
link_class | CSS class for auto-generated links |
tag_class | CSS class for tag links |
prev_next_class | CSS class for previous/next page links |
default_layout_name | The fallback layout when no other applies |
default_link_name | The partial used for generating link lists |
max_image_width | Maximum width for optimized images (in pixels) |
image_quality | WebP quality for optimized images |
responsive_image_widths | Widths Swifty generates for responsive image srcset candidates |
responsive_image_sizes | Default sizes attribute added to responsive local images |
default_og_image | Fallback social image; relative paths require site_url |
highlight_theme | Bundled highlight.js theme used by fenced code blocks |
minify | Enable or disable all output minification |
minify_html | Enable or disable HTML minification |
minify_css | Enable or disable CSS minification |
minify_js | Enable or disable Terser JavaScript compression (identifiers are not mangled) |
morphing | Enable Idiomorph-powered same-origin page transitions |
prefetching | Prefetch likely pages on hover, focus, or touch intent |
morph_target | CSS selector for the element Swifty morphs between pages |
search | Generate /search.json for client-side search |
search_content_limit | Maximum normalized content characters stored per search entry |
search_results_limit | Maximum results shown by the built-in search partial |
summary_length | Maximum length of automatically generated page summaries |
related_pages_limit | Maximum related pages selected for each tagged page |
words_per_minute | Reading speed used for estimates; fenced code is excluded |
build_concurrency | Maximum concurrent page and asset work during a build |
server_port | Port used by the local development server |
livereload_port | Port used by the LiveReload server |
watcher_delay | Delay used to debounce writes before rebuilding |
watcher_interval | Filesystem polling interval when polling is enabled |
watcher_use_polling | Use polling for both Chokidar and LiveReload instead of native events |
navigation_cache_size | Maximum pages retained by the browser navigation cache |
navigation_cache_ttl | Browser navigation cache lifetime in seconds |
page_count | Optional number of items per page; pagination is disabled when omitted |
pagination_class | CSS class for the pagination container |
pagination_link_class | CSS class for pagination links |
pagination_current_class | CSS class for the current page indicator |
date_sort_order | Sort order for pages by date: "desc" (newest first) or "asc" |
date_locale | Locale used for deterministic display dates (default: en-GB) |
timezone | IANA timezone used for display and date-only publishing (default: UTC) |
dateFormat | How dates are formatted throughout your site |
There is no implicit pagination size. Set page_count in root configuration or a folder's config.yaml to enable pagination for that scope.
Native filesystem events are the development default. Enable watcher_use_polling for cloud-synced folders, network mounts, container volumes, or other filesystems where swifty start misses changes:
watcher_use_polling: true
watcher_interval: 500
Syntax Highlighting Themes
Fenced code blocks are highlighted with a self-hosted highlight.js stylesheet. The default is monokai-sublime; choose another theme by its bundled filename without .css or .min.css:
highlight_theme: github-dark
For example, a11y-light, atom-one-dark, github, and github-dark are available. Invalid or unsafe names fail configuration validation. Swifty emits a fingerprinted local asset under /swifty/ and only links it from pages that actually contain a highlighted fenced code block.
Dates and Timezones
Swifty formats every display date with date_locale, timezone, and dateFormat, so the same source produces the same HTML on local machines and in CI. Calendar dates such as 2026-07-18 publish at midnight at the start of that day in timezone. Exact timestamps must include Z or an offset and retain their precise instant:
date_locale: en-GB
timezone: Europe/London
Machine-readable feed and sitemap dates always use Date or ISO values; Swifty does not parse formatted display strings back into metadata.
Using Config Values in Pages
Any config value can be dropped into your pages or layouts using ERB-style syntax:
Welcome to <%= sitename %>!
Written by <%= author %>.
Simple as that.
JavaScript Extension Hooks
Most customization belongs in layouts, partials, data files, and Eta expressions. When a site needs a small reusable helper or Markdown rule, add an optional swifty.config.js at the project root:
module.exports = {
globals: {
productName: "Sweet Crumbs",
},
helpers: {
uppercase(value) {
return String(value).toUpperCase();
},
},
markedExtensions: [{
extensions: [{
name: "highlightText",
level: "inline",
start(source) {
return source.indexOf("==");
},
tokenizer(source) {
const match = /^==([^=]+)==/.exec(source);
if (match) {
return { type: "highlightText", raw: match[0], text: match[1] };
}
},
renderer(token) {
return `<mark>${token.text}</mark>`;
},
}],
}],
};
Globals and helpers are available directly in Eta expressions:
<h2><%= uppercase(productName) %></h2>
markedExtensions accepts the same extension objects passed to marked.use(). If the site's package.json sets "type": "module", use export default { ... } instead of module.exports. Extension configuration is loaded when Swifty starts; restart swifty start after editing it. Core config and page values take precedence over extension globals with the same name.
Base Path Deployments
Set base_path when the generated site is hosted below the domain root, such as a GitHub Pages project site:
site_url: https://example.github.io
base_path: /my-project
Swifty prefixes generated and authored root-relative URLs, feeds, sitemap entries, images, CSS, and JavaScript. Output still builds directly into dist/; deploy that directory at /my-project.
Morph Navigation
By default, Swifty's reusable Morpheus client fetches same-origin HTML links and morphs the configured page target with Idiomorph. It keeps the outer layout in place while updating the URL, title, focus, and scroll position. Existing swifty:* browser events and data-swifty-* controls remain available as compatibility aliases for the generic morpheus:* API.
morphing: true
prefetching: true
morph_target: main
Set prefetching: false if you want morphing without hover/focus/touch prefetches. Set morphing: false to return to plain browser navigation.
turbo is deprecated in Swifty 3. Use morphing and prefetching instead.
Folder-Level Config
Here's a neat trick: you can add a config.yaml inside any folder in pages/ to set defaults for all pages in that folder. Great for giving a whole section its own author or layout without repeating yourself.
Client-Side Search
Swifty generates /search.json by default. Add its built-in, self-hosted search interface to any page or layout with one line:
<%= partial: search %>
It provides weighted results, keyboard navigation, accessible status updates, and base-path-aware URLs without an external dependency. Control the displayed result count with search_results_limit (10 by default). A project-level partials/search.html or partials/search.md overrides the built-in interface.
The index contains each searchable page's title, URL, summary, normalized text content, and tags. URLs include base_path automatically. Normalized content is limited to 5,000 leading characters per entry by default so the initial search download stays bounded; change that with search_content_limit. Titles, summaries, tags, and URLs remain complete in their separate weighted fields.
The file has this shape:
{
"version": 1,
"pages": [
{
"title": "Getting Started",
"url": "/docs/get-started",
"summary": "Install and build your first Swifty site.",
"content": "Getting Started Install Swifty...",
"tags": ["docs", "tutorial"]
}
]
}
Set search: false in root configuration to disable the index, or in a page's front matter to exclude only that page. Drafts, scheduled pages, 404 pages, generated tag pages, pagination pages, and pages with sitemap: false are not included in production search results.
RSS Feeds
Want to offer RSS feeds for your blog or news section? Easy. Just add the folders you want feeds for:
site_url: https://yoursite.com
rss_feeds:
- blog
- news
This generates /blog/rss.xml and /news/rss.xml automatically, including all pages within those folders.
Custom Feed Options
Need more control? Use the expanded format:
rss_feeds:
- folder: blog
title: My Awesome Blog
description: Thoughts on code, coffee, and chaos
- folder: news
title: Company Updates
description: The latest from our team
RSS Config Options
| Option | What it does |
|---|---|
site_url | Required for RSS. The full URL of your site (used for absolute links in feeds) |
rss_feeds | List of folders to generate feeds for |
rss_max_items | Maximum items per feed (default: 20) |
language | Feed language code (default: "en") |
Each feed includes the page title, URL, publication date, and a description snippet. Feeds are sorted by date with the newest items first. Description snippets are rendered and normalized, so Markdown headings, links, emphasis, and template syntax do not leak into feed text.