Performance Guide
Optimize TokiForge for maximum performance in production applications.
Bundle Size Optimization
Tree-Shaking
TokiForge is built with tree-shaking in mind. Import only what you need:
// ✅ Good - tree-shakeable
import { ThemeRuntime, TokenExporter } from '@tokiforge/core';
// ❌ Bad - imports everything
import * as TokiForge from '@tokiforge/core';Framework Adapters
Each framework adapter adds ~2-5KB (gzipped):
| Package | Size (minified + gzipped) |
|---|---|
| @tokiforge/core | ~8KB |
| @tokiforge/vue | ~3KB |
| @tokiforge/react | ~2.5KB |
| @tokiforge/angular | ~4KB |
Recommendation: Only install the adapter you need.
Runtime Performance
Static vs Dynamic Mode
Choose the right mode for your use case:
Static Mode
Best for: Simple websites, static sites, minimal theme switching
provideTheme(config, {
mode: 'static',
bodyClassPrefix: 'theme',
});Pros:
- ⚡ Faster initial load (no CSS injection)
- 📦 Smaller runtime overhead
- 🎨 Pre-generated CSS classes
Cons:
- Limited to class-based switching
- No runtime token access
Performance: ~0ms overhead
Dynamic Mode
Best for: Complex apps, frequent theme changes, runtime token access
provideTheme(config, {
mode: 'dynamic',
selector: ':root',
prefix: 'hf',
});Pros:
- 🔥 Full token access
- 🎯 CSS variable updates
- 🚀 Smooth transitions
Cons:
- Slight overhead for CSS injection
Performance: ~5-10ms initial setup, <1ms for theme changes
Lazy Loading Themes
For apps with many themes, lazy load them:
const config = {
themes: [
{
name: 'light',
tokens: lightTokens, // Loaded immediately
},
{
name: 'dark',
tokens: async () => {
const { default: darkTokens } = await import('./themes/dark');
return darkTokens;
},
},
{
name: 'ocean',
tokens: async () => {
const { default: oceanTokens } = await import('./themes/ocean');
return oceanTokens;
},
},
],
};Benefits:
- 📉 Reduced initial bundle size
- ⚡ Faster first load
- 🎯 Load themes on-demand
Trade-off: Slight delay when switching to lazy-loaded theme (first time only)
Token Memoization
TokiForge automatically memoizes token lookups and CSS generation:
// First call - generates CSS (5-10ms)
runtime.applyTheme('dark', ':root', 'hf');
// Subsequent calls - uses cache (<1ms)
runtime.applyTheme('dark', ':root', 'hf');Cache invalidation: Automatic when themes change
SSR Optimization
Server-Side Theme Detection
Prevent flash of unstyled content (FOUC):
Vue/Nuxt
// server/middleware/theme.ts
export default defineEventHandler((event) => {
const themeCookie = getCookie(event, 'theme') || 'light';
event.context.theme = themeCookie;
});<!-- app.vue -->
<script setup>
const { theme } = useRequestEvent();
provideTheme(config, {
defaultTheme: theme,
});
</script>Next.js
// app/layout.tsx
import { cookies } from 'next/headers';
import { ThemeProvider } from '@tokiforge/nextjs';
export default function RootLayout({ children }) {
const theme = cookies().get('theme')?.value || 'light';
return (
<html>
<body>
<ThemeProvider config={config} initialTheme={theme}>
{children}
</ThemeProvider>
</body>
</html>
);
}Result: Zero flash, seamless hydration
Build Performance
Token Compilation
For large token sets, pre-compile tokens:
// build/compile-tokens.ts
import { TokenExporter } from '@tokiforge/core';
import { writeFileSync } from 'fs';
const css = TokenExporter.exportCSS(tokens, {
selector: ':root',
prefix: 'hf',
});
writeFileSync('public/tokens.css', css);Benefits:
- 🏗️ Faster builds (cached)
- 📦 Smaller runtime bundle
- ⚡ No runtime compilation
Parallel Processing
For multiple themes:
import { TokenExporter } from '@tokiforge/core';
import { writeFile } from 'fs/promises';
const themes = ['light', 'dark', 'ocean', 'sunset'];
await Promise.all(
themes.map(async (themeName) => {
const css = TokenExporter.exportCSS(themeTokens[themeName], {
selector: `[data-theme="${themeName}"]`,
prefix: 'hf',
});
await writeFile(`public/themes/${themeName}.css`, css);
})
);Benchmarks
Theme Switching Performance
Tested on modern hardware (M1 MacBook Pro):
| Operation | Static Mode | Dynamic Mode |
|---|---|---|
| Initial Load | 0ms | 8ms |
| Theme Switch | 2ms | 0.5ms |
| Token Access | N/A | <0.1ms |
Bundle Sizes (Production)
| App Type | TokiForge + Adapter | Impact |
|---|---|---|
| Small SPA | ~10KB (gzipped) | +1.5% |
| Medium App | ~12KB (gzipped) | +0.8% |
| Large App | ~15KB (gzipped) | +0.3% |
Optimization Checklist
Development
- [ ] Use tree-shakeable imports
- [ ] Choose appropriate mode (static vs dynamic)
- [ ] Lazy load non-essential themes
- [ ] Pre-compile tokens for production
Production
- [ ] Enable minification
- [ ] Use gzip/brotli compression
- [ ] Implement SSR theme detection
- [ ] Cache theme CSS files
- [ ] Use CDN for static assets
Monitoring
- [ ] Track theme switch timings
- [ ] Monitor bundle size
- [ ] Measure First Contentful Paint (FCP)
- [ ] Check Largest Contentful Paint (LCP)
Advanced Techniques
Virtual CSS
For extremely large token sets (1000+ tokens), use virtual CSS:
// Only inject currently visible tokens
const visibleTokens = filterVisibleTokens(allTokens, viewport);
runtime.applyTokens(visibleTokens);Web Workers
Offload token processing to web workers:
// worker.ts
import { TokenExporter } from '@tokiforge/core';
self.onmessage = ({ data }) => {
const css = TokenExporter.exportCSS(data.tokens, data.options);
self.postMessage({ css });
};// main.ts
const worker = new Worker('./worker.ts');
worker.postMessage({ tokens, options });
worker.onmessage = ({ data }) => {
injectCSS(data.css);
};Use case: Processing very large token sets without blocking main thread
Performance Tips
Colors
// ✅ Fast - hex colors
color: { value: '#3b82f6' }
// ⚠️ Slower - computed colors
color: { value: 'rgb(59, 130, 246)' }Tokens Organization
// ✅ Fast - shallow nesting
{
button: {
background: { value: '#...' }
}
}
// ⚠️ Slower - deep nesting
{
components: {
interactive: {
buttons: {
variants: {
primary: {
background: { value: '#...' }
}
}
}
}
}
}Recommendation: Max 3-4 levels of nesting
Measuring Performance
Browser DevTools
performance.mark('theme-start');
await runtime.applyTheme('dark');
performance.mark('theme-end');
performance.measure('theme-switch', 'theme-start', 'theme-end');
const measure = performance.getEntriesByName('theme-switch')[0];
console.log(`Theme switch took ${measure.duration}ms`);Lighthouse
Run Lighthouse audits:
- Performance score should remain >90
- First Contentful Paint <1.8s
- Time to Interactive ❤️.9s
Real-World Example
Before Optimization
// All themes loaded upfront
const config = {
themes: [light, dark, ocean, sunset, retro, neon],
};
// Bundle size: 45KB
// Initial load: 120msAfter Optimization
// Only essential theme loaded
const config = {
themes: [
{ name: 'light', tokens: lightTokens },
{ name: 'dark', tokens: () => import('./dark') },
{ name: 'ocean', tokens: () => import('./ocean') },
// ... rest lazy loaded
],
};
// Static mode for simple use case
provideTheme(config, { mode: 'static' });
// Pre-compiled CSS in production
// <link rel="stylesheet" href="/themes/light.css">
// Bundle size: 12KB (-73%)
// Initial load: 8ms (-93%)Summary
Key Takeaways:
- Choose static mode for simple apps, dynamic for complex ones
- Lazy load non-essential themes
- Implement SSR theme detection
- Pre-compile tokens in production
- Monitor performance with DevTools
Expected Performance:
- Initial load: <10ms
- Theme switch: <1ms
- Bundle impact: <15KB
👉 Next: Best Practices | Troubleshooting