@md/cf-page
An opinionated, HTML-close web framework for creating Cloudflare Pages with full local development support.
Getting Started
Ensure you have Deno installed and then run:
deno run -A jsr:@md/cf-page init This will:
- Prompt you for a domain name
- Download the starter template
- Extract it to a new directory
- Show you links to create a GitHub repository and set up Cloudflare Pages
Features
β‘ TypeScript Transpilation & Bundling
Automatic TypeScript compilation with full bundling support using Deno's native bundler.
Features:
- Automatic TypeScript to JavaScript transpilation
- Module bundling with dependency resolution using data URI encoding
- Minification enabled automatically in production builds
- Support for both local and remote imports (JSR, npm, HTTP)
- Type checking during build process
- Self-contained builds with base64 encoded dependencies
How it works:
- Transpilation: TypeScript files are compiled to modern JavaScript
- Bundling: All dependencies are resolved and bundled into a single file
- Remote imports: External dependencies are fetched and included in the bundle
- Production optimization: Automatic minification reduces file size for production builds
Example
// src/main.ts
import { serve } from "https://deno.land/std/http/server.ts";
import { helper } from "./utils.ts";
export default function() {
return helper("Hello TypeScript!");
}This TypeScript file will be automatically transpiled, bundled with its dependencies, and served as optimized JavaScript.
π¨ Tailwind CSS
Built-in Tailwind CSS 4 support for utility-first styling. See the Tailwind documentation for complete class reference and usage patterns.
Features:
- Tailwind CSS 4 integration
- Automatic class detection and purging
- Custom component layers support
- Dark mode variants
ποΈ File Minification
Intelligent file optimization that automatically reduces file sizes in production builds while preserving functionality and maintaining development-friendly source code.
Production-Only Minification:
- CSS: PostCSS with cssnano plugin for optimal compression
- JavaScript: Built-in minification via Deno's bundler
- TypeScript: Compiled and minified during bundling process
CSS Minification Details:
- Removes unnecessary whitespace and comments
- Optimizes CSS rules and declarations
- Merges identical selectors and rules
- Shortens color values and units where possible
- Only runs in production builds to maintain readable development code
JavaScript Minification:
- Variable name shortening and code compression
- Dead code elimination removes unused functions
- Integrated with the bundling process for optimal results
- Preserves source functionality while reducing bundle size
Development vs Production
// Development: Readable, unminified code
.header {
background-color: #ffffff;
padding: 1rem 2rem;
}
// Production: Minified and optimized
.header{background:#fff;padding:1rem 2rem}Minification only occurs during production builds, keeping your development experience fast and debuggable.
π Automatic Cache-Busting
Intelligent cache-busting system that automatically adds version parameters to asset URLs, ensuring users always receive the latest versions while enabling aggressive browser caching.
Supported Elements:
- Scripts:
<script src="...">tags - Stylesheets:
<link rel="stylesheet">tags - Images:
<img src="...">tags - Media:
<video>,<audio>,<source>,<track>elements
Hash Generation Process:
- Content-based hashing using file contents for accuracy
- Base64URL encoding for URL-safe hash values
- Handles relative (`./`, `../`) and absolute (`/`) paths
- Only processes local assets, skips external URLs
- Rehype plugin integration for reliable HTML processing
Before and After
<!-- Before cache-busting -->
<link rel="stylesheet" href="/style.css">
<script src="./main.js"></script>
<img src="../images/logo.png" alt="Logo">
<!-- After cache-busting -->
<link rel="stylesheet" href="/style.css?v=abc123def456">
<script src="./main.js?v=xyz789uvw012"></script>
<img src="../images/logo.png?v=mno345pqr678" alt="Logo">Query parameters are automatically added based on file content hashes. When files change, new hashes ensure cache invalidation.
Benefits:
- Aggressive Caching: Set long cache headers without fear of stale content
- Instant Updates: Content changes immediately invalidate caches
- CDN Optimization: Perfect compatibility with CDN caching strategies
- Zero Configuration: Works automatically on all supported asset types
π Compile-time HTML Templating
Advanced slot-based templating system with layout inheritance that processes templates at build time, ensuring zero runtime overhead and optimal performance.
Core Features:
- Layout Inheritance: Hierarchical
+layout.htmlfiles - Named Slots: Content injection with
<slot name="..."> - Default Content: Fallback content when slots aren't filled
- Directory-based: Automatic layout discovery walking up the file tree
- Compile-time Processing: Templates resolved during build, not runtime
Layout Structure Example
<!-- src/+layout.html -->
<!DOCTYPE html>
<html>
<head>
<title><slot name="title">Default Title</slot></title>
</head>
<body>
<header><slot name="header"></slot></header>
<main><slot>Default main content</slot></main>
<footer><slot name="footer">Β© {cf:year}</slot></footer>
</body>
</html>Page Content Example
<!-- src/about/index.html -->
<slot name="title">About Us</slot>
<slot name="header">
<nav><a href="/">Home</a></nav>
</slot>
<h1>About Our Company</h1>
<p>This content goes into the default slot.</p>How it Works:
- Layout Discovery: Walks up directory tree to find nearest +layout.html
- String Processing: Processes templates before HTML parsing to preserve structure
- Slot Injection: Replaces
elements with matching content - Default Handling: Uses slot default content when no replacement is provided
- Build-time Resolution: All templates fully resolved during build process
Benefits:
- Zero Runtime Cost: Templates compiled away during build
- SEO Friendly: Generates complete, static HTML files
- Maintainable: Shared layouts reduce code duplication
- Flexible: Named slots allow precise content placement
π Translation Engine
Built-in multi-language support with YAML translation files, parameter interpolation, and conditional rendering capabilities.
Features:
- YAML-based translation files (
+lang.yml) - Parameter interpolation with flexible syntax
- Markdown support: Use
{@md key}directive for formatted content - Conditional rendering:
{#if condition}and{#else}blocks - Array iteration:
{#each array as item}loops - Cascading translation override system
- Auto-detection of supported languages from root
+lang.yml - Global and page-specific translations
- Configuration via
$defaultLanguagekey in language files - Multi-language Build: Generates separate versions for each language (e.g.,
/en/,/de/)
Parameter Syntax
Translation values can include parameters that are replaced at build time. The syntax supports multiple parameter types:
<!-- Basic syntax: {key, param1: value1, param2: value2} -->
<!-- String literal parameters -->
<h1>{greeting, name: "John Doe"}</h1>
<!-- Output: Hello John Doe! -->
<!-- Translation key references -->
<h1>{welcome, name: {siteName}}</h1>
<!-- Output: Welcome to My Site! -->
<!-- Nested translation keys -->
<footer>{footer.copyright, year: {cf:year}}</footer>
<!-- Output: Copyright Β© 2025 mdressler. All rights reserved. -->
<!-- Mixed parameter types -->
<p>{message, user: {userName}, time: "10:30 AM", date: {cf:year}}</p>
<!-- Output: User John Doe logged in at 10:30 AM on 2025 -->
<!-- Built-in variables -->
<div lang="{cf:lang}">{cf:path}</div>
<!-- Output: <div lang="en">/about.html</div> -->Parameter Types:
- String literals: Enclosed in double quotes:
"value" - Translation keys: Enclosed in braces:
{key}or{nested.key} - Built-in variables:
{cf:lang}(current language),{cf:path}(current path),{cf:year}(current year)
More Parameter Examples
The parameter syntax is flexible and supports complex use cases:
<!-- Language file with nested structure -->
# src/+lang.yml
en:
messages:
greeting: "Hello {user}, welcome to {app}!"
footer:
copyright: "Β© {year} {company}. All rights reserved."
products:
price: "${amount} USD"
<!-- Using nested keys with parameters -->
<h1>{messages.greeting, user: "Alice", app: {siteName}}</h1>
<!-- Output: Hello Alice, welcome to My Site! -->
<!-- Multiple levels of nesting -->
<footer>{footer.copyright, year: {cf:year}, company: "ACME Corp"}</footer>
<!-- Output: Β© 2025 ACME Corp. All rights reserved. -->
<!-- Combining with conditionals -->
{#if isOnSale}
<span class="price">{products.price, amount: "49.99"}</span>
{#else}
<span class="price">{products.price, amount: "99.99"}</span>
{/if}Conditional & Loop Syntax
Use conditional blocks and loops to show or hide content and iterate over arrays:
<!-- Conditional rendering based on translation values -->
{#if userLoggedIn}
<p>Welcome back, {userName}!</p>
<a href="/logout">Logout</a>
{#else}
<p>Please log in to continue.</p>
<a href="/login">Login</a>
{/if}
<!-- Array iteration with markdown support -->
{#each menuItems as item}
<li>
<a href="/{item}">{navigation[item]}</a>
<!-- Use @md directive to process markdown in array items -->
<p>{@md item} section with [additional info](/{item}/info)</p>
</li>
{/each}
<!-- Conditional content based on feature flags -->
{#if featureEnabled}
<div class="new-feature">
<h3>π New Feature Available!</h3>
<p>{newFeatureDescription}</p>
</div>
{/if}Note: Conditional blocks work with any translation key that resolves to a truthy/falsy value. Loop blocks work with arrays. To enable markdown processing within these blocks, use the {@md key} directive (see Markdown Processing section below).
Language Files
Create a +lang.yml file in your source directory to define languages and translations:
# src/+lang.yml
$defaultLanguage: en
global:
siteName: "My Site"
userLoggedIn: false
featureEnabled: true
menuItems: ["home", "about", "contact"]
en:
welcome: "Welcome to {name}!"
userName: "John Doe"
newFeatureDescription: "Try our new dashboard with enhanced analytics!"
navigation:
home: "Home"
about: "About"
contact: "Contact"
de:
welcome: "Willkommen bei {name}!"
userName: "Johann MΓΌller"
newFeatureDescription: "Probieren Sie unser neues Dashboard mit erweiterten Analysen!"
navigation:
home: "Startseite"
about: "Γber uns"
contact: "Kontakt"Markdown Processing
Process markdown-formatted content from translation values using the {@md key} directive:
<!-- Language file content -->
# src/+lang.yml
en:
boldText: "This is **very important**"
linkText: "Visit [our website](https://example.com)"
codeExample: "Use the `npm install` command"
richContent: "This has **bold** and `code` and [links](/) all together"
<!-- HTML template -->
<!-- Regular placeholders - markdown NOT processed -->
<p>{boldText}</p>
<!-- Outputs: This is **very important** -->
<!-- Markdown directive - markdown IS processed -->
<p>{@md boldText}</p>
<!-- Outputs: This is <strong>very important</strong> -->
<p>{@md linkText}</p>
<!-- Outputs: Visit <a href="https://example.com">our website</a> -->
<p>Command: {@md codeExample}</p>
<!-- Outputs: Command: Use the <code>npm install</code> command -->
<div>{@md richContent}</div>
<!-- Outputs: This has <strong>bold</strong> and <code>code</code> and <a href="/">links</a> all together -->Important: Markdown processing only occurs when using the {@md key} directive. Regular translation placeholders like {key} will output raw markdown text without processing. This explicit approach gives you full control over when markdown formatting is applied.
Using Translations in HTML
<!-- src/index.html -->
<h1>{welcome, name: {siteName}}</h1>
<!-- Conditional user interface -->
{#if userLoggedIn}
<p>Welcome back, {userName}!</p>
<a href="/logout">Logout</a>
{#else}
<p>Please log in to continue.</p>
<a href="/login">Login</a>
{/if}
<!-- Dynamic navigation with arrays -->
<nav>
<ul>
{#each menuItems as item}
<li>
<a href="/{item}">{navigation[item]}</a>
<!-- Use @md directive to process markdown formatting -->
<small>{@md item} section</small>
</li>
{/each}
</ul>
</nav>
<!-- Feature flag example -->
{#if featureEnabled}
<div class="feature-banner">
<h3>π New Feature Available!</h3>
<p>{newFeatureDescription}</p>
</div>
{/if}πΌοΈ Automatic Image Optimization
Intelligent image processing for optimal web delivery without quality loss.
Optimizations:
- Lossless PNG compression
- JPG quality optimization
- SVG minification and cleaning
- Automatic format selection
π¨ SASS/SCSS Transpilation
Complete SASS and SCSS preprocessing support using the official Dart Sass compiler, providing all modern SASS features with automatic compilation to optimized CSS.
Supported Syntax:
- SCSS: CSS-compatible syntax with braces and semicolons (
.scss) - SASS: Indented syntax without braces (
.sass) - CSS Imports: Standard CSS
@importstatements - SASS Imports:
@useand@forwardfor module system
Advanced Features:
- Variables: CSS custom properties and SASS variables
- Mixins: Reusable style patterns with parameters
- Functions: Custom functions and built-in SASS functions
- Nesting: Hierarchical rule nesting with parent selectors
- Control Flow:
@if,@for,@each,@while - Module System: Modern
@useand@forwardimports
Example SCSS Usage
// src/styles.scss
$primary-color: #f48120;
$border-radius: 0.5rem;
@mixin button-style($bg-color) {
background: $bg-color;
border-radius: $border-radius;
padding: 0.5rem 1rem;
&:hover {
background: darken($bg-color, 10%);
}
}
.btn-primary {
@include button-style($primary-color);
}Compiled automatically to optimized CSS with all SASS features resolved.
Compilation Process:
- Dart Sass Engine: Uses the official, most up-to-date SASS implementation
- Automatic Detection: Processes
.scssand.sassfiles automatically - Import Resolution: Resolves imports and dependencies correctly
- Error Reporting: Clear error messages with line numbers and context
- Hot Reload Integration: Works seamlessly with CSS hot reloading
Development Benefits:
- Modern SASS: Latest SASS features and syntax support
- Fast Compilation: Dart Sass performance optimizations
- Full Documentation: All SASS documentation applies
- Production Ready: Integrates with minification and optimization pipeline
π Live Reload
Advanced development server with intelligent file watching that automatically rebuilds and refreshes your browser when source files change, featuring smart CSS hot-reloading and build error reporting.
File System Monitoring:
- Source Directory Watching: Monitors entire srcDir for changes
- Intelligent Debouncing: Prevents excessive rebuilds during rapid file changes
- Selective Rebuilding: Only rebuilds affected files and dependencies
- Abort Controller Integration: Cancels ongoing builds when new changes detected
Browser Communication:
- Server-Sent Events: Uses /listen endpoint for real-time updates
- Client Script Injection: Automatically injects reload script into HTML pages
- Event Polling: Client polls for file change events
- Smart Refresh Logic: Differentiates between CSS-only and full page reloads
Performance Monitoring:
- Build time measurement and reporting
- Per-file processing time tracking
- Performance bottleneck identification
- Build error detection and console reporting
Development Experience
// Start the development server
deno run -A jsr:@md/cf-page serve
// File change detected: src/style.css
// β Rebuilding...
// β Build complete (23ms)
// β Browser refreshed (CSS only)
// File change detected: src/index.html
// β Rebuilding...
// β Build complete (156ms)
// β Browser refreshed (full page)The development server provides detailed feedback about build times and refresh types, helping you understand your development workflow performance.
Smart Reload Types:
- CSS Hot Reload: Updates stylesheets without page refresh for .css, .scss, .sass
- Full Page Reload: Complete refresh for HTML, JavaScript, and other asset changes
- Error Recovery: Automatically retries failed builds and reports errors
π₯ Hot CSS Reload
Advanced CSS hot-reloading that intelligently updates stylesheets without page refresh, providing the fastest possible development feedback loop.
Supported File Types:
- CSS: Direct stylesheet updates (.css)
- SCSS/SASS: Preprocessed styles (.scss, .sass)
- Tailwind: Utility-first CSS framework integration
- PostCSS: All PostCSS-processed stylesheets
Hot Reload Process:
- File Detection: Identifies CSS-only changes vs. other file types
- Stylesheet Removal: Removes existing elements from DOM
- Cache Busting: Adds timestamp parameters to prevent browser caching
- Re-injection: Adds updated stylesheets back to document head
Technical Implementation
// Hot CSS reload process
1. File change detected: src/style.scss
2. SCSS compiled to CSS
3. Browser receives "reload-css" event
4. Client script execution:
- Remove: link[rel="stylesheet"]
- Add: timestamp to URLs (?t=1234567890)
- Re-inject: updated stylesheets
5. Styles update instantly β‘The entire process typically completes in under 50ms, providing near-instantaneous feedback.
Development Workflow:
- Instant Feedback: See styling changes immediately without losing context
- Rapid Iteration: Perfect for fine-tuning layouts and animations
- Cross-browser Testing: Multiple browser windows stay in sync
- Complex State Testing: Style modal dialogs, complex forms without re-setup
β‘ Cloudflare Function Emulation
@md/cf-page provides full local emulation of Cloudflare Pages Functions. Files named +fn.ts become API endpoints with the same API as production.
Local Development Features:
- Perfect Emulation: Full
EventContextobject with request, env, and utilities - HTTP Methods: Support for all HTTP method handlers
- Environment Variables: Access via
context.envjust like in production - Assets API: Local
context.env.ASSETSserving - Cloudflare-compatible request/response handling
Quick Example
// src/api/hello/+fn.ts
export const onRequest: PagesFunction = async (context) => {
return new Response("Hello World!");
};For complete API documentation, see Cloudflare Pages Functions API Reference.
π Cloudflare Middleware Emulation
@md/cf-page supports Cloudflare Pages Middleware with full local emulation. Files named +middleware.ts run before pages and functions.
Local Middleware Features:
- Directory-based Execution: Middleware hierarchy matches Cloudflare's path matching
- Request Context: Full
context.next()and context sharing support - Authentication Patterns: Perfect for protecting routes and handling auth
- Production Parity: Same execution order as Cloudflare Pages
Quick Example
// src/admin/+middleware.ts
export const onRequest: PagesFunction = async (context) => {
const isAuthenticated = checkAuth(context.request);
if (!isAuthenticated) {
return new Response("Unauthorized", { status: 401 });
}
return context.next();
};For complete middleware documentation, see Cloudflare Pages Middleware Guide.
Deploying to Cloudflare Pages
The easiest way to deploy your @md/cf-page project is by connecting it to GitHub. Follow these steps to get started:
- Create a new GitHub repository
- Create a new Cloudflare Page
- Link the repository you just created
- Set the build command to
curl -fsSL https://cf-page.mdressler.dev/build.sh | bash - Set the output directory to
dist - "Save and Deploy" your page
- Push your code to the repository using the instructions from GitHub
- You can then also enable build caching for faster builds
Configuration
Configure @md/cf-page by adding settings to your deno.jsonc or deno.json file under the @md/cf-page key:
{
"name": "my-site",
"tasks": { /** Your build and dev tasks */ },
"@md/cf-page": {
"port": 3000,
"srcDir": "./src",
// Any configuration overrides; see options below
}
}
Configuration Options
port
Type: number | Default: 3000
Port for the development server.
srcDir
Type: string | Default: "./src"
Directory containing your source files (converted to absolute path).
outDir
Type: string | Default: "./dist"
Directory where built files are output (converted to absolute path).
bundle
Type: boolean | Default: true
Whether to bundle TypeScript/JavaScript files.
npmToEsm
Type: boolean | Default: true
Convert npm: specifiers to esm.sh CDN URLs for browser compatibility.
functionName
Type: string | Default: "+fn"
File name for Cloudflare Functions (without extension).
middlewareName
Type: string | Default: "+middleware"
File name for Cloudflare Middleware (without extension).
layoutName
Type: string | Default: "+layout.html"
File name for HTML layout templates.
langfileName
Type: string | Default: "+lang.yml"
File name for YAML language/translation files.
pluginName
Type: string | Default: "+plugin.ts"
File name for YAML language/translation files.
watchDirs
Type: string[] | Default: []
Other directories for which changes should rebuild the dev server.
bindings
Type: {[envVarName: string]: { type: "D1" | "KV"; id: string }} | Default: {}
Any bindings that should be provided.
ignore
Type: Array | Default: ["**/.DS_Store"]
Glob patterns to match files that should NOT be part of the final output.
absoluteLinks
Type: boolean | Default: true
Convert relative local links (e.g. href, src) to absolute ones.
markdownToHTML
Type: boolean | Default: true
Convert .md files to .html.
API Reference
CLI Commands
init - Initialize a new project with the starter template.
deno run -A jsr:@md/cf-page initserve - Start the development server with live reload.
deno run -A jsr:@md/cf-page servebuild - Build the project for production or development.
deno run -A jsr:@md/cf-page build