API Reference
Complete API documentation for Vitto.
Plugin Options
vitto(options: VittoOptions)
The main Vitto plugin function.
import vitto from 'vitto';
export default defineConfig({
plugins: [
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
// other options
}),
],
}); VittoOptions
Complete configuration options for the Vitto plugin.
interface VittoOptions {
metadata: Metadata;
pagesDir?: string;
layoutsDir?: string;
partialsDir?: string;
minify?: boolean | Partial<MinifyOptions>;
enableSearchIndex?: boolean;
pagefindOptions?: Partial<PagefindServiceConfig>;
outputStrategy?: 'html' | 'directory';
dynamicRoutes?: DynamicRouteConfig[];
hooks?: Record<string, HookFunction>;
assets?: { main: string; css: string[] };
ventoOptions?: Partial<VentoOptions>;
} metadata (Required)
- Type:
Metadata - Required: Yes
Site metadata to inject into all page templates.
interface Metadata {
siteName: string; // Required: Site name
title: string; // Required: Default page title
description?: string; // Optional: Site description
keywords?: string[] | string; // Optional: SEO keywords
author?: string; // Optional: Site author
language?: string; // Optional: Site language
[key: string]: any; // Optional: Any custom metadata
} Example:
vitto({
metadata: {
siteName: 'My Awesome Site',
title: 'Welcome to My Site',
description: 'A website built with Vitto',
keywords: ['vitto', 'static-site', 'vite'],
author: 'Your Name',
language: 'en',
// Custom metadata
social: {
twitter: '@johndoe',
github: 'johndoe',
},
theme: {
primaryColor: '#007bff',
darkMode: true,
},
},
}); Access in Templates:
<title>{{ metadata.title }}</title>
<meta name="description" content="{{ metadata.description }}">
<meta name="author" content="{{ metadata.author }}">
<html lang="{{ metadata.language }}">
{{# Access custom metadata #}}
<a href="https://twitter.com/{{ metadata.social.twitter }}">Twitter</a> pagesDir
- Type:
string - Default:
'src/pages'
Directory containing page templates (.vto files).
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
pagesDir: 'src/pages',
}); layoutsDir
- Type:
string - Default:
'src/layouts'
Directory containing layout templates.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
layoutsDir: 'src/layouts',
}); partialsDir
- Type:
string - Default:
'src/partials'
Directory containing partial templates.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
partialsDir: 'src/partials',
}); minify
- Type:
boolean | Partial<MinifyOptions> - Default:
false
Enable HTML minification. Set to true for defaults or pass custom options.
// Simple boolean
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
minify: true,
});
// Custom options
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
minify: {
collapseWhitespaces: 'conservative',
removeComments: true,
minifyCss: { lib: 'lightningcss' },
minifyJs: true,
},
}); MinifyOptions:
interface MinifyOptions {
collapseBooleanAttributes?: boolean;
collapseWhitespaces?: 'none' | 'conservative' | 'aggressive';
minifyCss?: { lib: 'esbuild' | 'lightningcss' };
minifyJs?: boolean;
minifyJson?: boolean;
normalizeAttributes?: boolean;
quotes?: boolean;
removeComments?: boolean | 'all' | 'some';
removeEmptyAttributes?: boolean;
removeEmptyMetadataElements?: boolean;
removeRedundantAttributes?: 'none' | 'some' | 'all';
selfClosingVoidElements?: boolean;
sortAttributes?: boolean;
sortSpaceSeparatedAttributeValues?: boolean;
tagOmission?: boolean;
} Default MinifyOptions:
{
collapseBooleanAttributes: true,
collapseWhitespaces: 'conservative',
minifyCss: { lib: 'lightningcss' },
minifyJs: true,
minifyJson: true,
normalizeAttributes: true,
quotes: true,
removeComments: false,
removeEmptyAttributes: false,
removeEmptyMetadataElements: false,
removeRedundantAttributes: 'all',
selfClosingVoidElements: false,
sortAttributes: true,
sortSpaceSeparatedAttributeValues: true,
tagOmission: false
} enableSearchIndex
- Type:
boolean - Default:
true
Enable Pagefind search index generation during build.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
enableSearchIndex: true,
}); pagefindOptions
- Type:
Partial<PagefindServiceConfig> - Default: See below
Configure Pagefind search indexing.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
pagefindOptions: {
rootSelector: 'main',
excludeSelectors: ['nav', 'footer'],
verbose: true,
},
}); PagefindServiceConfig:
interface PagefindServiceConfig {
rootSelector?: string; // Element to index (default: 'html')
excludeSelectors?: string[]; // Selectors to exclude from indexing
forceLanguage?: string; // Force specific language
verbose?: boolean; // Enable verbose logging
keepIndexUrl?: boolean; // Keep index URL structure
writePlayground?: boolean; // Generate playground (dev only)
glob?: string; // Glob pattern for files to process
} Default PagefindOptions:
{
rootSelector: 'html',
writePlayground: false,
keepIndexUrl: true,
verbose: false
} outputStrategy
- Type:
'html' | 'directory' - Default:
'html'
Output file strategy for generated pages.
'html':about.vto→about.html→/about.html'directory':about.vto→about/index.html→/about/
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
outputStrategy: 'directory',
}); dynamicRoutes
- Type:
DynamicRouteConfig[] - Default:
[]
Configure dynamic route generation.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
dynamicRoutes: [
{
template: 'post',
dataSource: 'posts',
getParams: (post) => ({ slug: post.slug }),
getPath: (post) => `blog/${post.slug}.html`,
},
],
}); DynamicRouteConfig:
interface DynamicRouteConfig {
template: string; // Template name (without .vto)
dataSource: string; // Hook name providing data array
pageSize?: number; // Items per page — enables paginated mode
getParams: (item: any) => Record<string, any>; // item or pageNum
getPath: (item: any) => string; // item or pageNum
} hooks
- Type:
Record<string, HookFunction> - Default:
{}
Register hook functions for data injection.
import { defineHooks } from 'vitto';
const postsHook = defineHooks('posts', async () => {
return await fetchPosts();
});
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
hooks: {
posts: postsHook,
},
}); HookFunction:
type HookFunction<T = any, P = any> = (params?: P) => T | Promise<T>; assets
- Type:
{ main: string; css: string[] } - Default: Auto-generated by Vite
Override Vite-generated assets. Rarely needed.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
assets: {
main: 'assets/main.js',
css: ['assets/style.css'],
},
}); ventoOptions
- Type:
Partial<VentoOptions> - Default:
{}
Pass custom options to Vento template engine.
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
ventoOptions: {
autoescape: true,
includes: ['custom/includes'],
filters: {
customFilter: (value) => value.toUpperCase(),
},
},
}); Hooks API
defineHooks(name, handler)
Define a hook function for data injection.
function defineHooks<T = any, P = any>(
name: string,
handler: (params?: P) => T | Promise<T>
): HookFunction<T, P>; Parameters:
name- Hook identifier (used in templates and config)handler- Function that returns data (can be async)
Returns: Hook function that can be registered
Example:
import { defineHooks } from 'vitto';
// Static hook
export const siteHook = defineHooks('site', () => {
return {
name: 'My Site',
url: 'https://example.com',
};
});
// Async hook
export const postsHook = defineHooks('posts', async () => {
const response = await fetch('https://api.example.com/posts');
return await response.json();
});
// Parameterized hook
export const postHook = defineHooks('post', async (params) => {
if (!params?.id) {
throw new Error('ID is required');
}
const response = await fetch(`https://api.example.com/posts/${params.id}`);
return await response.json();
}); Type Parameters:
T- Return type of the hookP- Parameter type (optional)
TypeScript Example:
Tip
Use TypeScript generics to get full type safety in your hooks and templates.
interface Post {
id: number;
title: string;
content: string;
slug: string;
date: string;
}
interface PostParams {
slug: string;
}
export const postHook = defineHooks<Post, PostParams>('post', async (params) => {
if (!params?.slug) {
throw new Error('Slug is required');
}
const response = await fetch(`/api/posts/${params.slug}`);
const post: Post = await response.json();
return post;
}); Template Functions
Functions available in Vento templates.
renderAssets()
Injects Vite-generated CSS and JavaScript assets.
Returns: string - HTML script and link tags
Usage:
<head>
{{ renderAssets() |> safe }}
</head> Output (Development):
<script type="module" src="/@vite/client"></script>
<script type="module" src="/src/main.ts"></script> Output (Production):
<link rel="stylesheet" href="/assets/style-abc123.css" />
<script type="module" src="/assets/main-def456.js"></script> include(path, data?)
Include a partial template.
Parameters:
path- Path to partial (relative topartialsDir)data- Optional data to pass to partial
Usage:
{{ include "partials/header.vto" }}
{{ include "partials/card.vto" {
title: "Card Title",
content: "Card content"
} }} layout(path)
Set the layout for a page.
Parameters:
path- Path to layout file (relative tolayoutsDir)
Usage:
{{ layout "layouts/base.vto" }}
<h1>Page Content</h1> Configuration Types
Metadata
Site metadata configuration.
interface Metadata {
siteName: string; // Required: Site name
title: string; // Required: Default page title
description?: string; // Optional: Site description
keywords?: string[] | string; // Optional: SEO keywords
author?: string; // Optional: Site author
language?: string; // Optional: Site language (e.g., 'en', 'es')
[key: string]: any; // Optional: Custom metadata fields
} Example:
const metadata: Metadata = {
siteName: 'Tech Blog',
title: 'Tech Blog - Latest Articles',
description: 'A blog about web development and technology',
keywords: ['web development', 'javascript', 'typescript'],
author: 'Jane Smith',
language: 'en',
// Custom fields
social: {
twitter: '@techblog',
github: 'techblog',
},
analytics: {
googleAnalytics: 'UA-XXXXX-Y',
},
}; DynamicRouteConfig
Configuration for dynamic route generation.
interface DynamicRouteConfig {
template: string; // Template name (without .vto)
dataSource: string; // Hook name providing data array
pageSize?: number; // Items per page — enables paginated mode
getParams: (item: any) => Record<string, any>; // item or pageNum
getPath: (item: any) => string; // item or pageNum
} Example:
{
template: 'post',
dataSource: 'posts',
getParams: (post) => ({
slug: post.slug,
id: post.id
}),
getPath: (post) => `blog/${post.slug}.html`
} PagefindServiceConfig
Pagefind search configuration.
interface PagefindServiceConfig {
rootSelector?: string; // Element to index (default: 'html')
excludeSelectors?: string[]; // Selectors to exclude from indexing
forceLanguage?: string; // Force specific language
verbose?: boolean; // Enable verbose logging
keepIndexUrl?: boolean; // Keep index URL structure
writePlayground?: boolean; // Generate playground (dev only)
glob?: string; // Glob pattern for files to process
} Example:
{
rootSelector: 'main',
excludeSelectors: ['nav', 'footer', '.sidebar', '.comments'],
forceLanguage: 'en',
verbose: true,
keepIndexUrl: true
} MinifyOptions
HTML minification options.
interface MinifyOptions {
collapseBooleanAttributes?: boolean;
collapseWhitespaces?: 'none' | 'conservative' | 'aggressive';
minifyCss?: {
lib: 'esbuild' | 'lightningcss';
};
minifyJs?: boolean;
minifyJson?: boolean;
normalizeAttributes?: boolean;
quotes?: boolean;
removeComments?: boolean | 'all' | 'some';
removeEmptyAttributes?: boolean;
removeEmptyMetadataElements?: boolean;
removeRedundantAttributes?: 'none' | 'some' | 'all';
selfClosingVoidElements?: boolean;
sortAttributes?: boolean;
sortSpaceSeparatedAttributeValues?: boolean;
tagOmission?: boolean;
} Defaults:
{
collapseBooleanAttributes: true,
collapseWhitespaces: 'conservative',
minifyCss: { lib: 'lightningcss' },
minifyJs: true,
minifyJson: true,
normalizeAttributes: true,
quotes: true,
removeComments: false,
removeEmptyAttributes: false,
removeEmptyMetadataElements: false,
removeRedundantAttributes: 'all',
selfClosingVoidElements: false,
sortAttributes: true,
sortSpaceSeparatedAttributeValues: true,
tagOmission: false
} Helper Functions
Template Context
Objects and data available in all templates.
metadata
Site metadata object (always available).
{{# Access metadata #}}
<title>{{ metadata.title }}</title>
<meta name="description" content="{{ metadata.description }}">
<meta name="author" content="{{ metadata.author }}">
<html lang="{{ metadata.language }}">
{{# Access custom metadata #}}
<a href="https://twitter.com/{{ metadata.social.twitter }}">Follow us</a> viteAssets
Object containing individual asset paths.
interface ViteAssets {
main: string; // Main JavaScript entry
css: string[]; // Array of CSS files
} Usage:
{{# Manual asset injection #}}
<script type="module" src="{{ viteAssets.main }}"></script>
{{ for css of viteAssets.css }}
<link rel="stylesheet" href="{{ css }}">
{{ /for }} Hook Data
All registered hooks are available by their name.
{{# From hooks: { site: siteHook, posts: postsHook } #}}
{{ site.name }}
{{ site.url }}
{{ for post of posts }}
{{ post.title }}
{{ /for }} Vento Filters
Note
Vento filters transform data in templates. Use the built-in filters before creating custom ones.
Built-in Vento filters available in templates.
safe
Renders HTML without escaping.
{{ htmlContent |> safe }}
{{ renderAssets() |> safe }} uppercase
Converts to uppercase.
{{ title |> uppercase }} lowercase
Converts to lowercase.
{{ text |> lowercase }} trim
Removes whitespace.
{{ text |> trim }} json
Converts to JSON string.
<script>
const data = {{ data |> json |> safe }};
</script> replace
Replaces text.
{{ text |> replace("old", "new") }}
{{ slug |> replace(" ", "-") }} Environment Variables
Access environment variables in templates and config.
In Templates
{{ if env.NODE_ENV === 'production' }}
<script src="analytics.js"></script>
{{ /if }}
{{ if env.VITE_FEATURE_FLAG === 'true' }}
<div class="new-feature">...</div>
{{ /if }} In Configuration
import { defineConfig } from 'vite';
export default defineConfig(({ mode }) => ({
plugins: [
vitto({
metadata: {
siteName: 'My Site',
title: 'My Site',
},
minify: mode === 'production',
enableSearchIndex: mode === 'production',
pagefindOptions: {
verbose: mode === 'development',
},
}),
],
})); In Hooks
export default defineHooks('config', () => {
return {
apiUrl: process.env.VITE_API_URL || 'http://localhost:3000',
environment: process.env.NODE_ENV,
debug: process.env.NODE_ENV === 'development',
};
}); Warning
Environment variables accessed in templates via env.* are evaluated at build time. They won’t update on the client side.
Type Exports
Types exported from the Vitto package.
import type {
VittoOptions,
Metadata,
DynamicRouteConfig,
PagefindServiceConfig,
MinifyOptions,
HookFunction,
} from 'vitto'; Example: Complete Type-Safe Configuration
import { defineConfig } from 'vite';
import vitto, { defineHooks } from 'vitto';
import type { VittoOptions, DynamicRouteConfig, Metadata } from 'vitto';
// Type-safe metadata
const metadata: Metadata = {
siteName: 'Tech Blog',
title: 'Tech Blog - Latest Articles',
description: 'A blog about web development',
keywords: ['blog', 'web development', 'javascript'],
author: 'Jane Doe',
language: 'en',
social: {
twitter: '@techblog',
github: 'techblog',
},
};
// Type-safe hook
interface Post {
id: number;
slug: string;
title: string;
content: string;
date: string;
author: string;
}
const postsHook = defineHooks<Post[]>('posts', async () => {
const posts: Post[] = await fetchPosts();
return posts;
});
const postHook = defineHooks<Post | null, { slug: string }>('post', async (params) => {
if (!params?.slug) return null;
const post: Post | undefined = await fetchPost(params.slug);
return post || null;
});
// Type-safe dynamic routes
const dynamicRoutes: DynamicRouteConfig[] = [
{
template: 'post',
dataSource: 'posts',
getParams: (post: Post) => ({ slug: post.slug }),
getPath: (post: Post) => `blog/${post.slug}.html`,
},
];
// Type-safe plugin options
const vittoOptions: VittoOptions = {
metadata,
pagesDir: 'src/pages',
layoutsDir: 'src/layouts',
partialsDir: 'src/partials',
minify: process.env.NODE_ENV === 'production',
enableSearchIndex: true,
outputStrategy: 'directory',
hooks: {
posts: postsHook,
post: postHook,
},
dynamicRoutes,
pagefindOptions: {
rootSelector: 'main',
verbose: false,
},
};
export default defineConfig({
plugins: [vitto(vittoOptions)],
}); CLI Commands
Commands available when using Vitto.
Development
npm run dev Starts Vite development server with hot module replacement.
Note: Search functionality is not available in development mode. Build the site to test search.
Build
npm run build Builds the site for production:
- Compiles templates
- Processes assets with Vite
- Generates dynamic routes
- Minifies HTML (if enabled)
- Generates search index (if enabled)
Preview
npm run preview Previews production build locally. This is where you can test search functionality.
Default Configuration
The complete default configuration:
const DEFAULT_OPTIONS: VittoOptions = {
// metadata is required - no default
pagesDir: 'src/pages',
layoutsDir: 'src/layouts',
partialsDir: 'src/partials',
minify: false,
assets: undefined, // Auto-generated by Vite
dynamicRoutes: [],
enableSearchIndex: true,
pagefindOptions: {
rootSelector: 'html',
writePlayground: false,
keepIndexUrl: true,
verbose: false,
},
outputStrategy: 'html',
}; Pagination Helpers
Types and helpers for paginated routes.
PaginatedData<T>
- Type: Interface
Shape of paginated data injected into templates.
interface PaginatedData<T> {
items: T[]; // Items for the current page
page: number; // Current page number (1-based)
pageSize: number; // Items per page
totalItems: number; // Total items across all pages
totalPages: number; // Total number of pages
hasNext: boolean; // Whether there is a next page
hasPrev: boolean; // Whether there is a previous page
prevUrl?: string | null; // URL to previous page
nextUrl?: string | null; // URL to next page
firstUrl?: string; // URL to first page
lastUrl?: string; // URL to last page
} PaginateOptions
- Type: Interface
Options passed to paginate().
interface PaginateOptions {
page: number; // Current page number (1-based)
pageSize: number; // Items per page
totalItems?: number; // Override total count (defaults to items.length)
} paginate<T>(items, options)
- Returns:
PaginatedData<T>
Slice an array into a single page of items. Used internally by the plugin; you normally do not need to call this directly.
import { paginate } from 'vitto';
const result = paginate(posts, { page: 1, pageSize: 10 });
// { items: [...10 items], page: 1, pageSize: 10, totalPages: 5, ... } Next Steps
- Examples - Real-world usage examples
- Troubleshooting - Common issues and solutions
- Contributing - Contribute to Vitto
- Configuration Guide - Detailed configuration guide