Need a custom directive, remote source, or build hook? This section covers Bengal's extension points. Most sites never need this — start with Authoring and Theming first.
Plugin authors should read Contributor Quickstart for dev setup, then pick an extension type below.
Extension Points
Bengal supports several extension mechanisms:
| Extension Type | Use Case | Difficulty |
|---|---|---|
| Build Hooks | Run external tools (Tailwind, esbuild) before/after builds | Easy |
| Theme Customization | Override templates and CSS | Easy |
| Content Collections | Type-safe frontmatter with schema validation | Moderate |
| Template Shortcodes | Add template-only embeds without Python | Easy |
| Custom Directives | Create new MyST directive blocks | Advanced |
| Custom Content Sources | Fetch content from APIs, databases, or remote services | Advanced |
Architecture Overview
Extensions integrate at different stages of the build pipeline:
Quick Start Examples
Build Hooks
Integrate external tools by adding hooks to yourbengal.toml:
[dev_server]
pre_build = [
"npx tailwindcss -i src/input.css -o assets/style.css"
]
post_build = [
"echo 'Build complete!'"
]
Theme Customization
Override any theme template by placing a file with the same name in your project'stemplates/directory:
your-project/
├── templates/
│ └── page.html # Overrides theme's page.html
└── bengal.toml
Content Collections
Define typed schemas for your content:
# collections.py
from dataclasses import dataclass
from datetime import datetime
from bengal.collections import define_collection
@dataclass
class BlogPost:
title: str
date: datetime
author: str = "Anonymous"
collections = {
"blog": define_collection(schema=BlogPost, directory="content/blog"),
}
Custom Directives
Create new directive blocks by implementing theDirectiveHandlerprotocol:
from typing import ClassVar
from patitas.nodes import Directive
class AlertDirective:
names: ClassVar[tuple[str, ...]] = ("alert",)
token_type: ClassVar[str] = "alert"
def parse(self, name, title, options, content, children, location):
return Directive(
location=location,
name=name,
title=title or "info",
options=options,
children=tuple(children),
)
def render(self, node, rendered_children, sb):
level = node.title or "info"
sb.append(f'<div class="alert alert-{level}">')
sb.append(rendered_children)
sb.append("</div>")
When to Extend
Choose the right extension mechanism for your needs:
- Build hooks: Integrate CSS preprocessors, JavaScript bundlers, or custom scripts
- Theme customization: Modify page layouts, add partials, or change styling
- Collections: Enforce frontmatter requirements and get IDE autocompletion
- Custom directives: Add domain-specific content blocks (alerts, embeds, widgets)
- Content sources: Pull content from GitHub, Notion, REST APIs, or databases
Related Resources
- Architecture Reference for understanding Bengal internals
- Protocol Layer for interface contracts
- Build Pipeline for pipeline phase details
- Configuration for
bengal.tomloptions