Skip to main content

plain.pages

Serve static pages, markdown, and assets from templates/pages directories.

Overview

The plain.pages package automatically discovers and serves static pages from templates/pages directories in your app and installed packages. Pages can be HTML, Markdown, redirects, or static assets, with support for frontmatter variables and template rendering.

# app/templates/pages/about.md
---
title: About Us
---

# About Our Company

We build great software.

This creates a page at /about/ that renders the markdown content with the title "About Us".

Pages are discovered from:

  • {package}/templates/pages/ for each installed package
  • app/templates/pages/ in your main application

The file path determines the URL:

  • index.html or index.md → /
  • about.html or about.md → /about/
  • docs/getting-started.md → /docs/getting-started/
  • styles.css → /styles.css (served as static asset)

Page types

HTML pages

HTML files are rendered as templates with access to the standard template context:

<!-- app/templates/pages/features.html -->
---
title: Features
---

<h1>{{ page.title }}</h1>
<p>Current user: {{ get_current_user() }}</p>

Markdown pages

Markdown files (.md) are automatically converted to HTML:

<!-- app/templates/pages/guide.md -->
---
title: User Guide
template_name: custom-page.html
---

# User Guide

This is **markdown** content with [links](/other-page/).

Redirect pages

Files with .redirect extension create redirects:

# app/templates/pages/old-url.redirect
---
url: /new-url/
status_code: 301
---

Assets

Any file that isn't HTML, Markdown, or a redirect is served as a static asset:

app/templates/pages/
├── favicon.ico
├── robots.txt
├── images/
│   └── logo.png
└── docs/
    └── guide.pdf

These are served at their exact paths: /favicon.ico, /images/logo.png, etc.

Template pages

Files containing .template. in their name are skipped and not served as pages. Use these for shared template fragments:

app/templates/pages/
├── base.template.html  # Not served
└── index.html          # Served at /

Serving raw markdown

You can optionally serve raw markdown content (without frontmatter) alongside rendered HTML pages. When enabled, markdown pages can be accessed as raw markdown via:

  1. Accept header negotiation - Send Accept: text/markdown or Accept: text/plain to get raw markdown
  2. Separate .md URLs - Access /docs/guide.md alongside /docs/guide/
# settings.py
PAGES_SERVE_MARKDOWN = True

With this setting enabled:

  • /docs/guide/ with Accept: text/html → Rendered HTML page
  • /docs/guide/ with Accept: text/markdown → Raw markdown content
  • /docs/guide.md → Raw markdown content (without frontmatter)

The raw markdown serves with text/plain content type, making it useful for:

  • External markdown processors
  • API consumers needing markdown source
  • Documentation tools that need raw content
  • Command-line tools like curl or httpie

Note: This feature is disabled by default. Only enable it if you need to serve raw markdown content.

Linking to markdown URLs

When markdown serving is enabled, you can link to the raw markdown version from templates:

<!-- In your page template -->
<a href="{{ page.get_markdown_url() }}">View Source</a>
<a href="{{ page.get_markdown_url() }}">Download Markdown</a>

The get_markdown_url() method returns:

  • The .md URL (e.g., /docs/guide.md) if the page is a markdown page or an HTML page with a companion .md file
  • None if the page has no markdown URL or the feature is disabled

Frontmatter

Pages support YAML frontmatter for configuration:

---
title: Custom Title
template_name: my-template.html
render_plain: true
custom_var: value
---

Available frontmatter options:

  • title: Page title (defaults to filename)
  • template_name: Custom template to use
  • render_plain: Skip template rendering (for markdown)
  • url: Redirect URL (for .redirect files)
  • status_code: Redirect status code (for .redirect files, defaults to 302)
  • Any custom variables accessible via page.vars

Custom views

You can extend the view classes to customize page rendering:

from plain.pages.views import PageView


class CustomPageView(PageView):
    def get_template_context(self):
        context = super().get_template_context()
        context["extra_data"] = self.get_extra_data()
        return context

The main view classes are:

Settings

Setting Default Env var
PAGES_SERVE_MARKDOWN False -

See default_settings.py for more details.

FAQs

How do I use a custom base template for markdown pages?

Set the template_name in frontmatter to specify your own template. Your template should include {{ page.content }} to render the markdown content.

Can I use template tags and filters in markdown files?

Yes. Unless you set render_plain: true in the frontmatter, markdown files are processed as templates first, then converted to HTML. You can use any template tags and filters available in your project.

How do I access frontmatter variables in templates?

Custom frontmatter variables are available through page.vars. For example, if your frontmatter includes author: Jane Doe, you can access it with {{ page.vars.author }}.

Why isn't my page showing up?

Check that your file is in a templates/pages/ directory and doesn't contain .template. in the filename. Template files are intentionally skipped.

Installation

Install the plain.pages package from PyPI:

uv add plain.pages

Metadata

Release files for plain.pages 0.20.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for plain.pages 0.20.0
File Size Uploaded
plain_pages-0.20.0.tar.gz 16.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for plain.pages 0.20.0
File Interpreter ABI Platform
plain_pages-0.20.0-py3-none-any.whl Python 3 none any Details

Total release size: 35.9 kB

Release files / plain_pages-0.20.0.tar.gz

Download URL plain_pages-0.20.0.tar.gz
Size 16.6 kB
Tags Source
SHA-256 checksum
How to use checksums
a70460e42d83c098c4534cc53a0b9f4c6c47665b238e09a944ead93bc1d6a463
BLAKE2b-256 checksum
How to use checksums
d35193645a9eae3818b2d880301beafce475f810a6099287583608d6e7fc106e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / plain_pages-0.20.0-py3-none-any.whl

Download URL plain_pages-0.20.0-py3-none-any.whl
Size 19.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
785d42c45f35db4fb2338e44a1bbd57639fdcdfefeed7a155307ea40be0f4078
BLAKE2b-256 checksum
How to use checksums
98833fe64dff50aa887239279315a027150efbc8b8525c5ba9d37c396b182852
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.17 {"installer":{"name":"uv","version":"0.12.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.20.0 This release

2 release files

0.19.8

2 release files

0.19.6

2 release files

0.19.5

2 release files

0.19.3

2 release files

0.19.2

2 release files

0.19.1

2 release files

0.18.4

2 release files

0.18.3

2 release files

0.18.1

2 release files

0.18.0

2 release files

0.17.0

2 release files

0.16.1

2 release files

0.16.0

2 release files

0.15.1

2 release files

0.15.0

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.12.2

2 release files

0.12.1

2 release files

0.12.0

2 release files

0.11.0

2 release files

0.10.5

2 release files

0.10.4

2 release files

0.10.3

2 release files

0.10.2

2 release files

0.10.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.5

2 release files

0.5.4

2 release files

0.5.3

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

0.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page