Markdown Pipeline
Stati uses Markdown-It as its core markdown processor, enhanced with carefully selected plugins and customization options. The pipeline is designed to be extensible and developer-friendly while supporting modern markdown features.
Core Features
Standard Markdown
Stati supports all standard Markdown syntax out of the box:
# Headings
## Subheadings
### And so on...
**Bold text** and _italic text_
[Links](https://example.com) and [internal links](/getting-started/introduction/)
- Unordered lists
- With multiple items
1. Ordered lists
2. With numbers
> Blockquotes for emphasis
> Can span multiple lines
`Inline code` and:
```javascript
// Code blocks with syntax highlighting
function hello() {
console.log('Hello, Stati!');
}
```
### Front Matter
Every markdown file can include YAML front matter:
```markdown
---
title: 'My Page Title'
description: 'A brief description for SEO'
date: '2024-01-15'
tags: ['stati', 'markdown', 'documentation']
author: 'John Doe'
draft: false
layout: 'post'
---
# Your content starts here...
Syntax Highlighting
Code blocks in markdown are rendered with language class attributes. To add syntax highlighting, you need to include a client-side library like Prism.js or Highlight.js in your templates.
Stati automatically adds language classes to code blocks:
<!-- Rendered output for code blocks -->
<pre><code class="language-typescript">
// Your code here
</code></pre>
To add syntax highlighting:
- Choose a highlighting library (Prism.js, Highlight.js, etc.)
- Include the library CSS and JS in your layout template
- Code blocks will be automatically highlighted client-side
Example with Prism.js:
<!-- In your layout.eta -->
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/prismjs/themes/prism.min.css">
<script src="https://cdn.jsdelivr.net/npm/prismjs/prism.min.js"></script>
</head>
Markdown example:
```typescript
interface StatiConfig {
site: {
title: string;
baseUrl: string;
};
markdown?: MarkdownConfig;
}
```
```css
.highlight {
background: #f0f8ff;
border-left: 4px solid #0066cc;
padding: 1rem;
}
```
```bash
npm install @stati/core
npm run build
```
Enhanced Features
Table Support
Create tables using standard markdown table syntax:
| Feature | Stati | Other SSGs |
| -------------- | -------------- | ----------- |
| TypeScript | ✅ First-class | ⚠️ Add-on |
| ISG | ✅ Built-in | ❌ Manual |
| esbuild | ✅ Integrated | ⚠️ Optional |
| Simplicity | ✅ Minimal | 📦 Varies |
Renders as:
| Feature | Stati | Other SSGs |
|---|---|---|
| TypeScript | ✅ First-class | ⚠️ Add-on |
| ISG | ✅ Built-in | ❌ Manual |
| esbuild | ✅ Integrated | ⚠️ Optional |
| Simplicity | ✅ Minimal | 📦 Varies |
Task Lists
Create interactive checkboxes:
- [x] Completed task
- [ ] Pending task
- [x] Another completed task
- [ ] Future task
- [x] Completed task
- [ ] Pending task
- [x] Another completed task
- [ ] Future task
Auto-linking
URLs and email addresses are automatically converted to links:
Visit https://stati.build for more information.
Contact us at [email protected] for support.
Typography Enhancements
Smart quotes, em-dashes, and ellipses:
"Smart quotes" and 'apostrophes'
En-dash (--) and em-dash (---)
Ellipsis (...)
Markdown Configuration
Base Configuration
Stati uses Markdown-It with the following default configuration:
- HTML tags - Enabled for raw HTML in markdown
- Linkify - Auto-convert URLs to clickable links
- Typographer - Smart quotes and other typography enhancements
// stati.config.js
import { defineConfig } from '@stati/core';
export default defineConfig({
markdown: {
plugins: [
'footnote', // markdown-it-footnote
['mark', { /* options */ }], // markdown-it-mark with options
]
},
});
Note: TOC extraction and heading anchor generation are built into Stati and enabled by default. You don’t need
markdown-it-anchorormarkdown-it-toc-done-rightplugins.
Note: You need to install the plugins separately via npm:
npm install markdown-it-footnote markdown-it-mark
Configuration
Basic Configuration
Configure markdown processing in stati.config.js:
import { defineConfig } from '@stati/core';
export default defineConfig({
markdown: {
// Plugins array - Stati auto-prepends 'markdown-it-'
plugins: [
'footnote', // Plugin name only
['emoji', { shortcuts: {} }], // Plugin with options
],
// Custom markdown-it configuration
configure: (md) => {
// Modify settings (html, linkify, typographer are enabled by default)
md.set({ breaks: true });
// Add custom rendering rules
md.renderer.rules.code_inline = (tokens, idx) => {
const token = tokens[idx];
return `<code class="inline-code">${token.content}</code>`;
};
},
// Enable/disable TOC extraction and heading anchors (default: true)
toc: true,
},
});
Note: HTML tags, linkify, and typographer are enabled by default in Stati and cannot be disabled. TOC extraction and heading anchors are also built-in.
Advanced Configuration
export default defineConfig({
markdown: {
// Custom renderer modifications
configure(md) {
// Add custom rules
md.renderer.rules.code_inline = (tokens, idx) => {
const token = tokens[idx];
return `<code class="inline-code">${token.content}</code>`;
};
// Add custom plugins
md.use(customPlugin, options);
// Modify existing rules
const defaultCodeBlockRule = md.renderer.rules.code_block;
md.renderer.rules.code_block = (tokens, idx, options, env) => {
const token = tokens[idx];
const langClass = token.info ? ` class="language-${token.info}"` : '';
return `<pre${langClass}><code>${token.content}</code></pre>`;
};
},
},
});
Custom Plugins
Creating a Plugin
// plugins/custom-alerts.js
function alertPlugin(md, options = {}) {
const defaultRender =
md.renderer.rules.blockquote_open ||
function (tokens, idx, options, env, renderer) {
return renderer.renderToken(tokens, idx, options);
};
md.renderer.rules.blockquote_open = function (tokens, idx, options, env, renderer) {
const token = tokens[idx];
const nextToken = tokens[idx + 1];
if (nextToken && nextToken.content.startsWith('[!')) {
const match = nextToken.content.match(/^\[!(.*?)\]/);
if (match) {
const alertType = match[1].toLowerCase();
return `<div class="alert alert-${alertType}">`;
}
}
return defaultRender(tokens, idx, options, env, renderer);
};
}
// Usage in config
export default defineConfig({
markdown: {
configure(md) {
md.use(alertPlugin);
},
},
});
Using Custom Alerts
> [!NOTE]
> This is a note alert.
> [!WARNING]
> This is a warning alert.
> [!TIP]
> This is a tip alert.
Content Processing
Front Matter Processing
Front matter is parsed and made available to templates:
---
title: 'Advanced Guide'
lastModified: '2024-01-15'
contributors: ['John', 'Jane']
difficulty: 'advanced'
estimatedTime: '30 minutes'
---
# Content here...
Note: Stati supports standard frontmatter fields like
title,description,tags,layout,date,order, anddraft. You can also add custom fields (likedifficulty,estimatedTime,lastModified,contributorsabove) which will be available in templates viastati.page.*.
Access in templates:
<article class="<%= stati.propValue('difficulty', `difficulty-${stati.page.difficulty}`) %>">
<header>
<h1><%= stati.page.title %></h1>
<div class="meta">
<span>Difficulty: <%= stati.page.difficulty %></span>
<span>Est. time: <%= stati.page.estimatedTime %></span>
<span>Last updated: <%= new Date(stati.page.lastModified).toLocaleDateString() %></span>
</div>
</header>
<div class="content">
<%~ stati.content %>
</div>
</article>
Content Excerpts
Stati does not currently provide built-in excerpt support. To create excerpts, you can:
- Add an
excerptfield to your frontmatter - Use custom filters to truncate content
- Process content in build hooks
Example using frontmatter:
---
title: 'My Post'
excerpt: 'This is a custom excerpt for the post.'
---
# My Post
Full content goes here...
Access in templates:
<p class="excerpt"><%= stati.page.excerpt %></p>
Performance Optimization
Build-time Processing
All markdown processing happens at build time, not at runtime. This means:
- Markdown is parsed and rendered to HTML during the build
- All plugins run once during build
- The output is static HTML with no runtime markdown processing
// This runs during build, not at runtime
export default defineConfig({
markdown: {
configure(md) {
// Expensive processing is done once during build
md.use(expensivePlugin, {
// Pre-computed options
dictionary: loadLargeDictionary(),
rules: compileComplexRules(),
});
},
},
});
Incremental Static Generation
Stati’s ISG system provides intelligent caching for rendered pages:
- Page caching - Rendered HTML is cached and only rebuilt when content or dependencies change
- Content hash tracking - Changes trigger rebuilds automatically
- Template dependency tracking - Updates to layouts or partials invalidate dependent pages
- TTL-based rebuilds - Configure how long pages stay cached
See the ISG Configuration documentation for details on cache TTL, aging rules, and invalidation strategies.
The markdown pipeline is one of Stati’s core features, providing powerful content processing with extensible plugins. Next, learn about Incremental Static Generation to understand how Stati handles caching and rebuilds.