Filesystem-based Routing

Stati uses filesystem-based routing, which means the structure of your site/ directory directly determines the URLs of your website. This approach is intuitive, predictable, and doesn’t require complex routing configuration.

Basic Routing

Files to URLs

The mapping from files to URLs is straightforward:

site/
├── index.md              → /
├── about.md               → /about/
├── contact.md             → /contact/
└── blog/
    ├── index.md           → /blog/
    ├── first-post.md      → /blog/first-post/
    └── second-post.md     → /blog/second-post/

Directory Structure

Every directory can have its own index.md file, which becomes the homepage for that section:

site/
├── index.md              → / (site homepage)
├── docs/
│   ├── index.md          → /docs/ (docs homepage)
│   ├── getting-started.md → /docs/getting-started/
│   └── api/
│       ├── index.md      → /docs/api/ (API section homepage)
│       └── reference.md  → /docs/api/reference/

URL Generation Rules

Clean URLs

Stati automatically generates clean URLs by:

  • Removing the .md file extension
  • Converting index files to directory URLs
  • Adding trailing slashes for consistency
about.md → /about/
blog/index.md → /blog/
blog/my-post.md → /blog/my-post/

Important: No Automatic Slug Transform ation

Stati preserves filenames exactly as written (minus the .md extension). There is NO automatic:

  • Lowercase conversion
  • Space/underscore to hyphen conversion
  • Special character removal
  • Slug beautification

This means you must use URL-safe filenames from the start:

Good (URL-safe):
about.md → /about/
my-post.md → /my-post/
getting-started.md → /getting-started/

⚠️ Problematic (not URL-safe):
About.md → /About/ (case-sensitive URL)
my_post.md → /my_post/ (underscore in URL)
my post.md → /my post/ (space in URL - breaks)
post!.md → /post!/ (special chars - problematic)

Filename Best Practices

To ensure clean, working URLs:

  1. Use lowercase: about.md not About.md
  2. Use hyphens for word separation: my-post.md not my_post.md or my post.md
  3. Avoid special characters: Use only letters, numbers, and hyphens
  4. Keep names descriptive but concise: getting-started.md not getting_started_with_our_platform.md

Nested Routing

Deep Nesting

Stati supports unlimited nesting levels:

site/
└── docs/
    └── guides/
        └── advanced/
            └── customization.md → /docs/guides/advanced/customization/

Section Organization

Organize content by topic, date, or any logical structure:

site/
├── blog/
│   ├── 2024/
│   │   ├── january/
│   │   │   └── new-year-post.md → /blog/2024/january/new-year-post/
│   │   └── february/
│   │       └── valentine-post.md → /blog/2024/february/valentine-post/
│   └── 2023/
│       └── archive/
│           └── old-post.md → /blog/2023/archive/old-post/

Special Files and Directories

Files and Directories Excluded from Routing

Stati excludes certain files and directories from URL generation:

Files and Folders Starting with _ (Underscore)

Critical Rule: Any file or directory starting with _ is excluded from routing:

site/
├── _partials/           ❌ Not routed - Partial templates
│   ├── header.eta
│   ├── footer.eta
│   └── components/
│       └── button.eta
├── _components/         ❌ Not routed - Reusable components
│   └── card.eta
├── _includes/           ❌ Not routed - Include files
│   └── analytics.eta
├── _data/               ❌ Not routed - Data files
│   └── config.json
├── _drafts/             ❌ Not routed - Draft content
│   └── upcoming-post.md
├── partials/            ✅ Routed - Creates /partials/ (no underscore)
│   └── content.md       ✅ Routed - Creates /partials/content/
└── published.md         ✅ Routed - Creates /published/

Non-Markdown Files

Only .md files become pages. All other file types are automatically excluded:

site/
├── layout.eta           ❌ Not routed - Template file (.eta)
├── config.js            ❌ Not routed - JavaScript file
├── styles.css           ❌ Not routed - Stylesheet
├── image.png            ❌ Not routed - Image file
├── .DS_Store            ❌ Not routed - Not markdown
├── .gitkeep             ❌ Not routed - Not markdown
├── README.md            ✅ Routed - Creates /readme/ (is markdown!)
└── index.md             ✅ Routed - Creates / homepage

Note: README.md files ARE routed because they are markdown files. If you don’t want a README to appear on your site, either:

  • Use a different extension: README.txt
  • Place it in an underscore folder: _docs/README.md
  • Mark it as draft in front matter: draft: true

Use Cases for Underscore Exclusion

Partial Templates:

_partials/
├── header.eta          # Site header
├── navigation.eta      # Main navigation
├── sidebar.eta         # Blog sidebar
└── footer.eta          # Site footer

Component Library:

_components/
├── button.eta          # Reusable button
├── card.eta            # Content cards
├── modal.eta           # Modal dialogs
└── forms/
    ├── input.eta       # Form inputs
    └── validation.eta  # Form validation

Data and Configuration:

_data/
├── site-config.json    # Site-wide settings
├── navigation.json     # Menu structure
├── authors.json        # Author information
└── categories.json     # Content categories

Development Files:

_drafts/                # Work-in-progress content
_temp/                  # Temporary build files
_backup/                # Content backups
_assets/                # Source assets (pre-processing)

This convention allows you to organize helper files, templates, and components without creating unwanted routes, keeping your URL structure clean and intentional.

Layout Files

layout.eta files are special template files that don’t generate routes but define the layout for their directory and subdirectories:

site/
├── layout.eta          # Root layout
├── index.md            → / (uses root layout)
├── blog/
│   ├── layout.eta      # Blog-specific layout
│   ├── index.md        → /blog/ (uses blog layout)
│   └── post.md         → /blog/post/ (uses blog layout)

Dynamic Content Organization

Date-based Organization

For blogs and news sites, organize by date:

site/
└── blog/
    ├── index.md
    ├── 2024/
    │   ├── 01/
    │   │   ├── 15-new-feature.md → /blog/2024/01/15-new-feature/
    │   │   └── 30-update.md → /blog/2024/01/30-update/
    │   └── 02/
    │       └── 14-valentine.md → /blog/2024/02/14-valentine/

Category-based Organization

For documentation or portfolio sites:

site/
├── projects/
│   ├── web-development/
│   │   ├── ecommerce-site.md → /projects/web-development/ecommerce-site/
│   │   └── portfolio.md → /projects/web-development/portfolio/
│   └── mobile-apps/
│       ├── ios-app.md → /projects/mobile-apps/ios-app/
│       └── react-native.md → /projects/mobile-apps/react-native/

Front Matter URL Override

You can completely override the automatic URL generation using the permalink field in front matter:

---
title: 'Custom URL Example'
permalink: '/custom-path/'
---

# This page will be available at /custom-path/

Important: When using permalink:

  • Must be a static string (no template variables or date formatting)
  • Must start with /
  • Should end with / for consistency
  • Completely replaces the file-path-based URL

Example use cases:

---
# Override deeply nested file path
# File: site/archive/2023/old-posts/article.md
# Without permalink: /archive/2023/old-posts/article/
# With permalink: /featured/article/
permalink: '/featured/article/'
---
---
# Shorten long category paths
# File: site/documentation/api-reference/authentication.md
# Without permalink: /documentation/api-reference/authentication/
# With permalink: /docs/auth/
permalink: '/docs/auth/'
---

Working with the Router

When linking between pages, use absolute paths from the site root:

Check out our [Getting Started page](/getting-started/introduction/) for more information.
Visit the [API documentation](/api/reference/) to learn more.

In templates, you can access routing information:

<nav>
  <% if (stati.page.url === '/') { %>
    <a href="/" class="active">Home</a>
  <% } else { %>
    <a href="/">Home</a>
  <% } %>

  <% if (stati.page.url.startsWith('/blog/')) { %>
    <a href="/blog/" class="active">Blog</a>
  <% } else { %>
    <a href="/blog/">Blog</a>
  <% } %>
</nav>

Generate breadcrumbs from the URL structure:

<nav class="breadcrumbs">
  <a href="/">Home</a>
  <%
  const parts = stati.page.url.split('/').filter(Boolean);
  let currentPath = '';
  %>
  <% parts.forEach((part, index) => { %>
    <% currentPath += '/' + part; %>
    <% if (index < parts.length - 1) { %>
      <span class="separator"></span>
      <a href="<%= currentPath %>/" class="breadcrumb-link">
        <%= part.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) %>
      </a>
    <% } else { %>
      <span class="separator"></span>
      <span class="current">
        <%= part.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()) %>
      </span>
    <% } %>
  <% }); %>
</nav>

Best Practices

URL Structure Design

  1. Keep URLs short and descriptive

    • Good: /docs/getting-started/
    • Avoid: /documentation/getting-started-with-our-amazing-platform/
  2. Use consistent naming conventions

    • All lowercase
    • Hyphens for word separation
    • Avoid underscores and special characters
  3. Organize by content hierarchy

    • Group related content together
    • Use logical nesting levels
    • Consider your site’s navigation structure

File Organization

  1. Use meaningful directory names

    site/
    ├── getting-started/    # Clear section purpose
    ├── tutorials/          # Obvious content type
    └── api-reference/      # Descriptive and specific
    
  2. Keep related content together

    site/
    └── blog/
        ├── index.md        # Blog homepage
        ├── layout.eta      # Blog-specific layout
        ├── _partials/      # Blog-specific components
        └── posts/          # All blog posts
    
  3. Use index files for section homepages

    • Always provide an index.md for directory sections
    • Use it to introduce the section and link to subsections
    • Make it the navigation hub for that area

SEO Considerations

  1. Descriptive URLs help SEO

    • /blog/how-to-optimize-website-performance/ is better than /blog/post-123/
  2. Consistent structure improves crawling

    • Use predictable patterns like /blog/yyyy/mm/post-name/
    • Maintain consistent depth levels when possible
  3. Clean URLs are user-friendly

    • /contact/ is cleaner than /contact.html
    • Trailing slashes provide consistency

Advanced Routing Patterns

Multi-language Sites

Organize content by language:

site/
├── en/
│   ├── index.md → /en/
│   └── about.md → /en/about/
├── es/
│   ├── index.md → /es/
│   └── acerca.md → /es/acerca/
└── fr/
    ├── index.md → /fr/
    └── apropos.md → /fr/apropos/

API Documentation

Structure API docs logically:

site/
└── api/
    ├── index.md → /api/ (API overview)
    ├── authentication.md → /api/authentication/
    ├── endpoints/
    │   ├── users.md → /api/endpoints/users/
    │   └── posts.md → /api/endpoints/posts/
    └── examples/
        ├── basic.md → /api/examples/basic/
        └── advanced.md → /api/examples/advanced/

Understanding filesystem-based routing is key to organizing your Stati site effectively. Next, learn about Templates & Layouts to understand how your content gets rendered.