Banner of complete-tutorial--creating-categories-and-subcategories-using-pages-in-pelican-2.jpg

Complete Tutorial: Creating Categories and Subcategories Using Pages in Pelican


Category: python

πŸ“… June 24, 2025   |   πŸ‘οΈ Views: 17

Introduction: Why Static Websites Can Beat Dynamic Ones

In the web development world, dynamic websites built with platforms like WordPress, Flask, or Laravel often get all the attention. They provide databases, admin dashboards, and real-time interactivity. However, there’s a quiet champion that often goes overlooked: the static website.

Static websites, like the ones built with Pelican, are incredibly fast, secure, and require very little maintenance. There’s no backend database to worry about, no server-side code that can break or get hacked, and the speed is unmatched because the server simply delivers pre-built HTML files.

What’s even better? You can simulate categories and subcategories using only pages and folders, without complex taxonomies or dynamic systems. Let me walk you through the process step-by-step, using my actual setup.


1. Setting Up Your Python Environment and Pelican Project

Let’s start from scratch.

Create a new Python environment: It’s always a good idea to isolate your project.

    
python3 -m venv env
source env/bin/activate
    

Install Pelican:

    
pip install "pelican[markdown]"
pip install pelican-sitemap pelican-related-posts pelican-neighbors pelican-render-math pelican-jinja2content
    

Create your Pelican project:

    
pelican-quickstart
    

You’ll be asked a few questions:

  • The site title
  • Your author name
  • The site URL (you can leave it empty for now)

Pelican will create a basic directory structure for you.

   
my-pelican-site/
β”œβ”€β”€ content/
β”‚   β”œβ”€β”€ articles/      # Optional if you write articles
β”‚   β”œβ”€β”€ pages/         # General pages (about, contact, etc.)
β”‚   β”œβ”€β”€ courses/       # Structured content for courses
β”‚   β”œβ”€β”€ images/        # Static images
β”‚   β”œβ”€β”€ pdfs/          # Static PDFs
β”‚   └── extra/         # Additional files
β”œβ”€β”€ output/            # Generated static site (HTML files) after building
β”œβ”€β”€ pelicanconf.py     # Main configuration file
β”œβ”€β”€ publishconf.py     # Production-specific settings (optional)
β”œβ”€β”€ tasks.py           # Optional automation script
β”œβ”€β”€ Makefile           # For quick commands like `make html` and `make serve`
β”œβ”€β”€ requirements.txt   # Python dependencies (optional but recommended)
β”œβ”€β”€ theme/             # Your custom Pelican theme
β”‚   β”œβ”€β”€ templates/     # Jinja2 HTML templates
β”‚   └── static/        # Theme-specific CSS, JS, images
└── pelican-plugins/   # Pelican plugins folder (if local)
    


2. Understanding the Key Components of a Pelican Project

Here’s what matters for our goal:

content/ β€” where you will write your pages, courses, articles, etc.

output/ β€” the generated static website (HTML files, ready to publish).

pelicanconf.py β€” the main configuration file.

themes/ β€” where you can customize the design of your website.

We will mostly focus on:

Organizing the content/ folder

Writing rich Markdown files with metadata

Configuring pelicanconf.py properly to simulate categories and subcategories using folders.


3. Setting Up Your Pelican Configuration

Here’s a stripped version of my real configuration focused on pages and folder hierarchy:

    

AUTHOR = 'your author name'
SITENAME = 'your site name'
SITEURL = ''

PATH = 'content'
TIMEZONE = 'UTC'
DEFAULT_LANG = 'en'

PLUGIN_PATHS = ['pelican-plugins']
PLUGINS = ['sitemap', 'related_posts', 'neighbors', 'render_math', 'jinja2content']

THEME = 'theme'
STATIC_PATHS = ['images', 'courses', 'extra', 'theme/static', 'pdfs']

PAGE_PATHS = ['pages', 'blogs', 'courses']

PAGE_URL = '{slug}.html'
PAGE_SAVE_AS = '{slug}.html'

PAGE_URLS = {'courses': 'courses/{subject}/{schoolyear}/{slug}.html'}
PAGE_SAVE_AS_TEMPLATES = {'courses': 'courses/{subject}/{schoolyear}/{slug}.html'}

DEFAULT_PAGINATION = 7

DIRECT_TEMPLATES = ['index', 'categories', 'tags', 'archives', 'courses']

    

Key Notes:

βœ”οΈ We tell Pelican where to find pages: 'pages', 'blogs', 'courses'

βœ”οΈ We build URLs for courses based on metadata: subject and school year.

βœ”οΈ We don’t use articles here because we’re simulating categories with pages.


4. How to Create a Page (with Metadata)

You can create a page by writing a Markdown file inside content/pages/ or content/courses/.

Here’s a basic example for a course page:

Create it as content/courses/mathematics/course-1.md

   
Title: Course 1
Date: 2025-06-24
Type: course
Subject: mathematics
SchoolYear: 2025
Save_as: courses/mathematics/2025/course-1.html
URL: courses/mathematics/2025/course-1.html

Welcome to the Calculus Course for the year 2025. This course covers the fundamentals of calculus...
    


βœ”οΈ The metadata at the top (Type, Subject, SchoolYear) is what allows us to identify a page type, category and subcategory and filter pages later in the templates

βœ”οΈ (URL, Save_as, ) gives us more control about saving the files

βœ”οΈ you can add other metadata if you want, like description, path to thumbnail..., and all will be accessible to the template

βœ”οΈ This page will automatically be saved as: output/courses/mathematics/2025/course-1.html

5. Creating the Illusion of Categories and Subcategories

Here’s the trick:

  • Subjects act as categories.
  • School years act as subcategories.

And it all comes from the metadata

When you build the site, Pelican will automatically generate:

    
output/
β”œβ”€β”€ courses/
β”‚   β”œβ”€β”€ mathematics/
β”‚   β”‚   β”œβ”€β”€ 2025/
β”‚   β”‚   β”‚   └── calculus.html
β”‚   β”‚   └── 2024/
β”‚   β”‚       └── algebra.html
β”‚   └── physics/
β”‚       └── 2025/
β”‚           └── mechanics.html
    


You’ve now created categories and subcategories without using Pelican’s built-in categories.


6. Building the Website

Run the following command to generate your site:

    
pelican content
    

Pelican will create the output/ folder containing your static site.

You can preview it using:

    
pelican --listen --autoreload --port 8001
    

Visit http://localhost:8001 to see your site in action.


7. Navigating the Structure on Your Website

Since we configured pelican to find a direct template courses we create it in the templates folder exactly named "courses.html"

and then , go crazy with your code, do something like:

    
<h2 class="mb-4">Browse All Courses</h2>

<div class="container mt-5">
    <h2 class="mb-4">Courses by Subject and School Year</h2>
    {% set subjects = pages | selectattr('metadata.type', 'equalto', 'course') | map(attribute='metadata.subject') | unique | sort %}
    <ul class="list-group">
        {% for subject in subjects %}
            <li class="list-group-item mb-3">
                <h4 class="mb-2">{{ subject | title }}</h4>
                {% set schoolyears = pages
                    | selectattr('metadata.type', 'equalto', 'course')
                    | selectattr('metadata.subject', 'equalto', subject)
                    | map(attribute='metadata.schoolyear')
                    | unique | sort %}
                <ul class="ml-4">
                    {% for year in schoolyears %}
                        <li class="mb-2">
                            <strong>{{ year.replace('-', ' ') | title }}</strong>
                            {% set courses = pages
                                | selectattr('metadata.type', 'equalto', 'course')
                                | selectattr('metadata.subject', 'equalto', subject)
                                | selectattr('metadata.schoolyear', 'equalto', year)
                                | sort(attribute='date', reverse=True) %}
                            <ul class="ml-4">
                                {% for course in courses %}
                                    <li class="mb-1">
                                        <a href="{{ SITEURL }}/{{ course.url }}" class="text-decoration-none text-primary">
                                            {{ course.title or course.metadata.coursetitle }}
                                        </a>
                                    </li>
                                {% endfor %}
                            </ul>
                        </li>
                    {% endfor %}
                </ul>
            </li>
        {% endfor %}
    </ul>
</div>
    


We go directly to http://localhost:8001/courses to see it in action.

pelican categories
Pelcan Generated Categories

8. Final Thoughts

By combining:

  • Rich metadata in your Markdown files

  • A clean folder hierarchy

  • Dynamic template filtering

You can build the illusion of a fully categorized, subcategorized website without using databases or complex backends.

It’s fast.

It’s secure.

It’s flexible.

You now have the power to create a blazing-fast static site that rivals, and in many ways outperforms, dynamic CMS platforms.

If you want to extend this system later, you can easily:

  • Add pagination

  • Track views

  • Create search pages using plugins like tipue_search


← Quran Search: A Feature rich and modern design