Incremental Static Generation (ISG)
Incremental Static Generation is one of Stati’s most powerful features. It intelligently tracks dependencies, caches build outputs, and only regenerates what has actually changed. This reduces build times, especially for large sites.
What is ISG?
ISG is a build optimization strategy that:
- Tracks dependencies between files and content
- Caches build outputs with intelligent invalidation
- Ages content to optimize cache retention
- Enables partial rebuilds for efficient development
Unlike traditional static site generators that rebuild everything, Stati only rebuilds what needs to be updated.
How ISG Works
Dependency Tracking
Stati automatically tracks relationships between files:
blog/index.md depends on:
├── blog/post-1.md
├── blog/post-2.md
├── blog/post-3.md
├── blog/layout.eta
└── _partials/post-preview.eta
When post-1.md changes:
✅ Rebuild blog/post-1/ (direct change)
✅ Rebuild blog/ (depends on post-1.md)
❌ Skip blog/post-2/ (no changes)
❌ Skip about/ (unrelated)
```text
### Cache Manifest
Stati maintains a cache manifest at `.stati/cache/manifest.json`:
```json
{
"entries": {
"/blog/": {
"inputsHash": "abc123",
"deps": ["blog/index.md", "blog/post-1.md", "blog/post-2.md"],
"tags": ["blog", "posts"],
"publishedAt": "2024-01-15T10:00:00Z",
"renderedAt": "2024-01-15T10:00:00Z",
"ttlSeconds": 21600
}
}
}
Build Process
- Content Discovery - Find all content files
- Dependency Analysis - Map relationships between files
- Cache Validation - Check if cached outputs are still valid
- Selective Rebuilding - Only process changed content
- Manifest Update - Update cache entries and dependencies
Configuration
Basic ISG Setup
Configure ISG in stati.config.js:
import { defineConfig } from '@stati/core';
export default defineConfig({
isg: {
// Enable ISG (default: true)
enabled: true,
// Cache TTL in seconds (default: 6 hours)
ttlSeconds: 21600,
// Maximum age cap in days (default: 365 days)
maxAgeCapDays: 365,
// Progressive TTL increases based on content age
aging: [
{ untilDays: 7, ttlSeconds: 21600 }, // 6 hours for week-old content
{ untilDays: 30, ttlSeconds: 86400 }, // 24 hours for month-old content
{ untilDays: 90, ttlSeconds: 259200 }, // 3 days for 3-month-old content
{ untilDays: 365, ttlSeconds: 604800 } // 7 days for year-old content
],
},
});
Cache Management
Manual Invalidation
Use the CLI to invalidate specific content:
# Invalidate by path
stati invalidate path:/blog/post-1/
# Invalidate by tag
stati invalidate tag:blog
# Invalidate entries rendered in the last 7 days
stati invalidate age:7days
# Force rebuild everything
stati invalidate
# Invalidate multiple targets (space-separated)
stati invalidate "path:/blog/ tag:navigation age:30days"
Programmatic Invalidation
Use the invalidate function programmatically in your build scripts or applications:
import { invalidate } from '@stati/core';
// Invalidate specific paths
await invalidate('path:/blog/');
// Invalidate by tag
await invalidate('tag:navigation');
// Invalidate entries rendered within the last 30 days (exact calendar arithmetic)
await invalidate('age:30days');
// Invalidate multiple targets
await invalidate('path:/blog/ tag:navigation age:30days');
// Clear all cache
await invalidate();
Cache Configuration
Basic Time-based Caching
ISG uses a single global configuration approach. All content shares the same TTL and aging settings:
export default defineConfig({
isg: {
// Global TTL for all content
ttlSeconds: 21600, // 6 hours
// Age-based TTL adjustments apply to all content
aging: [
{ untilDays: 7, ttlSeconds: 1800 }, // 30 minutes for fresh content
{ untilDays: 30, ttlSeconds: 3600 }, // 1 hour for week-old content
{ untilDays: 90, ttlSeconds: 86400 }, // 24 hours for month-old content
],
},
});
Aging Configuration
The aging configuration uses a simple array format:
export default defineConfig({
isg: {
aging: [
{ untilDays: 7, ttlSeconds: 21600 }, // 6 hours for week-old content
{ untilDays: 30, ttlSeconds: 86400 }, // 24 hours for month-old content
{ untilDays: 90, ttlSeconds: 259200 }, // 3 days for 3-month-old content
{ untilDays: 365, ttlSeconds: 604800 } // 7 days for year-old content
],
},
});
Age Format for Invalidation
When invalidating by age, use these formats:
stati invalidate age:30days
stati invalidate age:2weeks
stati invalidate age:6months
stati invalidate age:1year
Important: Age calculations use exact calendar arithmetic:
- Days/Weeks: Simple day arithmetic (1 week = 7 days)
- Months: Exact month arithmetic using
setMonth()- handles varying month lengths (28-31 days) - Years: Exact year arithmetic using
setFullYear()- properly handles leap years
This means age:3months goes back exactly 3 calendar months (e.g., from Oct 1st to Jul 1st), not approximately 90 days.
Build Efficiency
How ISG Helps
Traditional SSG (full rebuild):
├── Every change: Rebuilds all pages
└── Large sites: Can be slow
Stati with ISG (incremental):
├── First build: Full build
├── Content change: Only affected pages
└── Layout change: Only pages using that layout
Development Experience
During development, ISG provides fast feedback:
- Initial build - Full build of all pages
- Content change - Only the changed page and its dependents rebuild
- Template change - All pages using that template rebuild
This means most edits during development result in significantly faster rebuilds compared to full site regeneration.
Cache Storage
Cache Directory Structure
.stati/
└── cache/
└── manifest.json # Cache metadata and entries
Cache Persistence
The .stati/cache/ directory stores build cache data between runs:
manifest.json- Tracks all cached pages and their metadata- Cache is invalidated when content or dependencies change
Hosting Considerations:
| Environment | Cache Behavior | Setup Required |
|---|---|---|
| Traditional servers | Persists on disk | None |
| CI/CD pipelines | Lost between runs | Cache .stati/ directory |
| Serverless (Vercel, Netlify) | Ephemeral filesystems | Use platform cache features |
| Docker containers | Lost on rebuild | Mount volume for .stati/ |
CI/CD Cache Examples:
# GitHub Actions
- uses: actions/cache@v4
with:
path: .stati/cache
key: stati-cache-${{ hashFiles('site/**') }}
restore-keys: stati-cache-
# GitLab CI
cache:
paths:
- .stati/cache/
For platforms with ephemeral filesystems, the first build will always be a full build. Subsequent builds within the same deployment or cached CI run will benefit from ISG.
Monitoring and Debugging
Build Output
Stati reports cache performance during builds, showing how many pages were generated vs cached and the cache hit rate. Use the --metrics flag for detailed build performance data.
ISG is central to Stati’s build workflow. Understanding how to configure and optimize it will improve your development experience and build times. Next, learn about Static Assets & Bundling to understand how Stati handles CSS, JavaScript, and other assets.