In this tutorial, you'll learn how to create a multi-part tutorial series with navigation between parts, progress tracking, and a series overview page.
Tip
Default Theme Navigation
Bengal's default theme includes prev/next navigation that works automatically:
- Section-scoped for
doc,tutorialtypes — usesweightfor ordering - Chronological for
blog,pagetypes — sorted by date - Enable with
navigation.prev_nextfeature flag
This tutorial shows how to use explicit series metadata for multi-part content with progress bars and "Part X of Y" displays.
Note
Who is this for? This guide is for content creators building courses, tutorials, or any multi-part content where readers progress through sequential lessons.
Goal
By the end of this tutorial, you will have:
- A multi-part tutorial series with proper frontmatter
- Prev/next navigation between parts
- A series overview page showing all parts
- Progress indicators showing completion status
- A reusable series navigation component
Prerequisites
- A Bengal site with some existing content
- Basic understanding of Kida templates
Steps
- 1
Structure Your Series Content
Set up the frontmatter for a multi-part tutorial series.
Let's create a 5-part tutorial series called "React from Scratch". First, create the directory structure:
mkdir -p content/tutorials/react-from-scratchCreate the series index at
content/tutorials/react-from-scratch/_index.md:--- title: "React from Scratch" description: "Build a complete React app from zero to deployment" type: series-index series_name: "React from Scratch" difficulty: intermediate estimated_time: "2 hours" prerequisites: - Basic JavaScript knowledge - Node.js installed --- :::{note} **Template Resolution** The `type: series-index` metadata tells Bengal to look for a template at `templates/series-index/list.html` (since `_index.md` files are section indexes). Bengal's template resolution follows this pattern: - **Section indexes** (`_index.md`): `{type}/list.html` → `list.html` → `index.html` - **Regular pages**: `{type}/single.html` → `single.html` → `page.html` If you prefer a different template path, you can use explicit `template:` frontmatter: ```yaml template: "series-overview.html" - Setting up a modern React development environment
- Component architecture and state management
- Styling approaches and best practices
- Testing your components
- Deploying to production
- Basic JavaScript knowledge (ES6+)
- Node.js 18+ installed
- A code editor (VS Code recommended)
- 3
Create the Series Overview Template
Build a landing page showing all parts in the series.
Create
templates/series-index/list.htmlfor the series overview page. Since_index.mdfiles are section indexes, Bengal's template resolution looks for{type}/list.htmltemplates.Tip
Template Resolution
When you set
type: series-indexin frontmatter, Bengal follows this template cascade:templates/series-index/list.html(for section indexes)templates/list.html(fallback)templates/index.html(final fallback)
If you prefer a different template name, use explicit
template:frontmatter:template: "series-overview.html"{% extends "default/base.html" %} {% block content %} <article class="series-overview"> <header class="series-overview-header"> <span class="series-badge">📚 Tutorial Series</span> <h1>{{ page.title }}</h1> {% if page.description %} <p class="lead">{{ page.description }}</p> {% end %} <div class="series-meta"> {% if page.metadata.difficulty %} <span class="meta-item"> <strong>Difficulty:</strong> {{ page.metadata.difficulty | title }} </span> {% end %} {% if page.metadata.estimated_time %} <span class="meta-item"> <strong>Time:</strong> {{ page.metadata.estimated_time }} </span> {% end %} {% if page.metadata.prerequisites %} <span class="meta-item"> <strong>Prerequisites:</strong> {{ page.metadata.prerequisites | join(', ') }} </span> {% end %} </div> </header> <div class="series-content"> {{ page.content | safe }} </div> {# List all parts #} <section class="series-parts"> <h2>All Parts</h2> <ol class="parts-list"> {% for part in section.pages | sort_by('weight') %} {% if part.series and part.series.name == page.metadata.series_name %} <li class="part-item"> <a href="{{ part.href }}" class="part-link"> <span class="part-number">Part {{ part.series.part }}</span> <span class="part-title">{{ part.title }}</span> {% if part.reading_time %} <span class="part-time">{{ part.reading_time }} min</span> {% end %} </a> </li> {% end %} {% end %} </ol> {% let first_part = section.pages | sort_by('weight') | first %} {% if first_part %} <a href="{{ first_part.href }}" class="btn btn-primary btn-large"> Start Learning → </a> {% end %} </section> </article> {% endblock %}Add styles:
.series-overview { max-width: 800px; margin: 0 auto; } .series-overview-header { text-align: center; padding-bottom: 2rem; border-bottom: 1px solid var(--border-color); margin-bottom: 2rem; } .series-overview-header h1 { font-size: 2.5rem; margin: 1rem 0; } .series-overview-header .lead { font-size: 1.25rem; color: var(--text-muted); max-width: 600px; margin: 0 auto; } .series-meta { display: flex; justify-content: center; gap: 2rem; margin-top: 1.5rem; font-size: 0.875rem; } .series-parts { margin-top: 3rem; } .parts-list { list-style: none; padding: 0; counter-reset: part; } .part-item { margin-bottom: 0.5rem; } .part-link { display: flex; align-items: center; padding: 1rem 1.5rem; background: var(--bg-secondary); border-radius: 8px; text-decoration: none; color: var(--text); transition: background 0.2s, transform 0.2s; } .part-link:hover { background: var(--bg-tertiary); transform: translateX(4px); } .part-number { width: 80px; font-size: 0.875rem; color: var(--accent); font-weight: 600; } .part-title { flex: 1; font-weight: 500; } .part-time { font-size: 0.875rem; color: var(--text-muted); } .btn-large { display: inline-block; margin-top: 2rem; padding: 1rem 2rem; font-size: 1.125rem; } - 5
Add Completion Celebration
Celebrate when readers finish the series.
Add a completion message to the series navigation for the last part:
{# At the end of series-nav.html #} {% if page.series and page.series.total > 0 and page.series.part == page.series.total %} <div class="series-complete-banner"> <div class="celebration">🎉</div> <h3>Congratulations!</h3> <p>You've completed "{{ page.series.name }}"</p> <div class="next-steps"> <h4>What's Next?</h4> <ul> <li><a href="/tutorials/">Browse more tutorials</a></li> {% if page.absolute_href %} <li><a href="{{ share_url('twitter', page) }}" target="_blank" rel="noopener">Share your achievement</a></li> {% end %} </ul> </div> </div> {% end %}Styles:
.series-complete-banner { text-align: center; padding: 2rem; background: linear-gradient(135deg, var(--color-success-bg), var(--bg-secondary)); border-radius: 12px; margin-top: 2rem; } .celebration { font-size: 3rem; margin-bottom: 1rem; } .series-complete-banner h3 { margin: 0; color: var(--color-success); } .series-complete-banner p { color: var(--text-muted); } .next-steps { margin-top: 1.5rem; text-align: left; display: inline-block; } .next-steps h4 { font-size: 0.875rem; margin-bottom: 0.5rem; } .next-steps ul { margin: 0; padding-left: 1.25rem; } .next-steps a { color: var(--accent); }
React from Scratch
Learn to build React applications from the ground up. This 5-part series takes you from zero to a fully deployed application.
What You'll Learn
Prerequisites
Now create each part. Here's **Part 1** at `content/tutorials/react-from-scratch/01-setup.md`:
```yaml
---
title: "Part 1: Project Setup"
nav_title: "1. Setup"
date: 2024-01-15
weight: 1
series:
name: "React from Scratch"
part: 1
total: 5
---
# Part 1: Project Setup
Let's set up our development environment and create our first React project.
## Create a New Project
```bash
npm create vite@latest my-react-app -- --template react
cd my-react-app
npm install
Understanding the Structure
After creation, you'll see...
[... rest of content ...]
Create **Parts 2-5** with the same structure, incrementing `part`:
```yaml
# Part 2: content/tutorials/react-from-scratch/02-components.md
---
title: "Part 2: Building Components"
weight: 2
series:
name: "React from Scratch"
part: 2
total: 5
---
# Part 3: content/tutorials/react-from-scratch/03-state.md
---
title: "Part 3: State Management"
weight: 3
series:
name: "React from Scratch"
part: 3
total: 5
---
# Part 4: content/tutorials/react-from-scratch/04-styling.md
---
title: "Part 4: Styling Your App"
weight: 4
series:
name: "React from Scratch"
part: 4
total: 5
---
# Part 5: content/tutorials/react-from-scratch/05-deployment.md
---
title: "Part 5: Deployment"
weight: 5
series:
name: "React from Scratch"
part: 5
total: 5
---
Tip
Naming Convention
Prefix files with numbers (01-,02-) to keep them sorted in your file manager. Useweightfor actual ordering.
:::
Summary
You now have a complete tutorial series system with:
- ✅ Multi-part content with series frontmatter
- ✅ Prev/next navigation with progress bar
- ✅ Series overview landing page
- ✅ Sidebar table of contents
- ✅ Completion celebration
Next Steps
- Series Navigation Recipe: Quick reference
- Build a Multi-Author Blog: Add author attribution
- Content Freshness: Show series age