# Create a Tutorial Series URL: /docs/tutorials/content/create-a-tutorial-series/ Section: content Tags: tutorial, intermediate, series, navigation -------------------------------------------------------------------------------- Create a Tutorial Series 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 Tip Default Theme Navigation Bengal's default theme includes prev/next navigation that works automatically: Section-scoped for doc, tutorial types — uses weight for ordering Chronological for blog, page types — sorted by date Enable with navigation.prev_next feature flag This tutorial shows how to use explicit series metadata for multi-part content with progress bars and "Part X of Y" displays. Note 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 1Structure Your Series Content5 min 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-scratch Create 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" 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 Setting up a modern React development environment Component architecture and state management Styling approaches and best practices Testing your components Deploying to production Prerequisites Basic JavaScript knowledge (ES6+) Node.js 18+ installed A code editor (VS Code recommended) 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 Tip Naming Convention Prefix files with numbers (01-, 02-) to keep them sorted in your file manager. Use weight for actual ordering. ::: 2Create the Series Navigation Component5 min Build a reusable prev/next navigation for series pages. Create templates/partials/series-nav.html: {# templates/partials/series-nav.html #} {% if page.series %} <nav class="series-nav" aria-label="Series navigation"> {# Series header with progress #} <div class="series-header"> <span class="series-badge">📚 Series</span> <h4 class="series-title">{{ page.series.name }}</h4> <div class="series-progress"> <div class="progress-bar"> <div class="progress-fill" style="width: {{ (page.series.part / page.series.total * 100) | round }}%"> </div> </div> <span class="progress-text"> Part {{ page.series.part }} of {{ page.series.total }} </span> </div> </div> {# Prev/Next links #} <div class="series-links"> {% if page.prev_in_series %} <a href="{{ page.prev_in_series.href }}" class="series-link prev"> <span class="link-direction">← Previous</span> <span class="link-title">{{ page.prev_in_series.title }}</span> </a> {% else %} <div class="series-link prev disabled"> <span class="link-direction">← Previous</span> <span class="link-title">Start of series</span> </div> {% end %} {% if page.next_in_series %} <a href="{{ page.next_in_series.href }}" class="series-link next"> <span class="link-direction">Next →</span> <span class="link-title">{{ page.next_in_series.title }}</span> </a> {% else %} <div class="series-link next completed"> <span class="link-direction">🎉 Complete!</span> <span class="link-title">You finished the series</span> </div> {% end %} </div> </nav> {% end %} Add the styles to assets/css/series.css: .series-nav { border: 1px solid var(--border-color); border-radius: 12px; padding: 1.5rem; margin: 2rem 0; background: var(--bg-secondary); } .series-header { text-align: center; margin-bottom: 1.5rem; } .series-badge { display: inline-block; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); } .series-title { margin: 0.5rem 0; font-size: 1.125rem; } .series-progress { margin-top: 1rem; } .progress-bar { height: 6px; background: var(--bg-tertiary); border-radius: 3px; overflow: hidden; } .progress-fill { height: 100%; background: var(--accent); transition: width 0.3s ease; } .progress-text { display: block; margin-top: 0.5rem; font-size: 0.875rem; color: var(--text-muted); } .series-links { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; } .series-link { padding: 1rem; border-radius: 8px; background: var(--bg); text-decoration: none; color: var(--text); transition: background 0.2s; } .series-link:hover:not(.disabled) { background: var(--bg-tertiary); } .series-link.disabled { opacity: 0.5; cursor: not-allowed; } .series-link.completed { background: var(--color-success-bg); opacity: 1; } .link-direction { display: block; font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text-muted); margin-bottom: 0.25rem; } .link-title { font-weight: 500; } .series-link.prev { text-align: left; } .series-link.next { text-align: right; } @media (max-width: 600px) { .series-links { grid-template-columns: 1fr; } } 3Create the Series Overview Template5 min Build a landing page showing all parts in the series. Create templates/series-index/list.html for the series overview page. Since _index.md files are section indexes, Bengal's template resolution looks for {type}/list.html templates. Tip Tip Template Resolution When you set type: series-index in 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; } 4Add Series Table of Contents Sidebar3 min Show all parts in a sidebar for easy navigation. Create templates/partials/series-toc.html: {# templates/partials/series-toc.html #} {% if page.series and page._section %} <aside class="series-toc"> <h4> <a href="{{ page._section.href }}">{{ page.series.name }}</a> </h4> <ol class="toc-parts"> {% let series_pages = page._section.pages | sort_by('weight') %} {% for part in series_pages %} {% if part.series and part.series.name == page.series.name %} <li class="{% if part.href == page.href %}current{% end %}"> <a href="{{ part.href }}"> <span class="part-num">{{ part.series.part }}.</span> {{ part.nav_title or part.title }} </a> </li> {% end %} {% end %} </ol> <div class="toc-progress"> {{ page.series.part }} / {{ page.series.total }} complete </div> </aside> {% end %} Styles: .series-toc { position: sticky; top: 2rem; padding: 1rem; background: var(--bg-secondary); border-radius: 8px; font-size: 0.875rem; } .series-toc h4 { margin: 0 0 1rem; font-size: 0.875rem; } .series-toc h4 a { color: var(--text); text-decoration: none; } .toc-parts { list-style: none; padding: 0; margin: 0; } .toc-parts li { margin-bottom: 0.25rem; } .toc-parts a { display: block; padding: 0.5rem; border-radius: 4px; text-decoration: none; color: var(--text-muted); } .toc-parts a:hover { background: var(--bg-tertiary); color: var(--text); } .toc-parts .current a { background: var(--accent); color: white; } .part-num { display: inline-block; width: 1.5rem; color: inherit; } .toc-progress { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border-color); text-align: center; color: var(--text-muted); } Include it in your series page layout: {% extends "default/base.html" %} {% block content %} <div class="series-layout"> <aside class="series-sidebar"> {% include "partials/series-toc.html" %} </aside> <article class="series-article"> <h1>{{ page.title }}</h1> {{ page.content | safe }} {% include "partials/series-nav.html" %} </article> </div> {% endblock %} 5Add Completion Celebration2 min 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); } 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 -------------------------------------------------------------------------------- Metadata: - Author: lbliii - Word Count: 1754 - Reading Time: 9 minutes