Hex.pm Documentation

Volt-powered static site generation for Elixir. Astral owns site semantics — pages, routes, Markdown, frontmatter, layouts, public files, and static HTML output — while Volt handles TypeScript, CSS, assets, dev-server integration, and HMR.

mix igniter.install astral
mix astral.dev
mix astral.build

Astral is intentionally separate from Volt. Volt remains the Vite-like frontend toolchain; Astral is the site framework built on top.

Why Astral

Static site generators often force site configuration, content rules, and frontend tooling into JavaScript. Astral keeps the site layer in ordinary Elixir while reusing Volt's BEAM-native asset pipeline.

You get:

  • Elixir astral.config.exs instead of JavaScript config objects.
  • Markdown pages rendered with MDEx and YAML frontmatter.
  • EEx layouts with @content, @page, @metadata, @route, and @site assigns.
  • Per-page layout selection through frontmatter.
  • Plain HTML pages for simple routes.
  • Public static files copied as-is.
  • TypeScript/CSS/assets built and served by Volt.
  • A Plug/Bandit dev server with Volt HMR client injection and full reloads for pages/layouts/public files.
  • Igniter-powered starter scaffolding.

Status

Astral is early, but the first release is useful for small static sites and documentation prototypes. Collections, feeds, sitemap generation, and richer routing are intentionally left for follow-up releases.

Installation

Install into an existing Mix project with Igniter:

mix igniter.install astral

Or add the dependency manually:

def deps do
  [
    {:astral, "~> 0.1.0"}
  ]
end

Then scaffold a starter site:

mix astral.new

The scaffold creates astral.config.exs, starter Markdown pages, an EEx layout, TypeScript/CSS assets, public files, tsconfig.json, and Volt JS/TS formatting/linting configuration.

Project layout

astral.config.exs
pages/
  index.md
  about.md
layouts/
  default.html
assets/
  app.ts
  styles.css
public/
  robots.txt

Configuration

Astral config is real Elixir and returns an %Astral.Config{} struct. No global app env is required for site settings.

# astral.config.exs
import Astral.Config

site do
  root "."
  outdir "dist"
  pages "pages"
  public "public"

  layouts "layouts" do
    default "default.html"
  end

  assets "assets" do
    entry "app.ts"
    url_prefix "/assets"
  end
end

Pages and frontmatter

Markdown pages are rendered with MDEx. YAML frontmatter is extracted by MDEx and decoded with YamlElixir:

---
title: About Astral
permalink: /about-us/
layout: default.html
---

# About

Output routes:

pages/index.md        -> dist/index.html
pages/about.md        -> dist/about/index.html
pages/blog/post.html  -> dist/blog/post/index.html

permalink overrides the default route. layout selects a layout from the layouts directory. Use layout: false to render without a layout.

Plain .html files in pages/ are supported too.

Layouts

Layouts are EEx templates. Use @content where page HTML should be inserted:

<!doctype html>
<html lang="en">
  <head>
    <title><%= @page.title || "Astral" %></title>
    <script type="module" src="<%= Astral.asset_path(@site, "app.ts") %>"></script>
  </head>
  <body>
    <main data-route="<%= @route %>">
      <%= @content %>
    </main>
  </body>
</html>

Available assigns:

  • @content — rendered page HTML.
  • @page%Astral.Content{} for the current page.
  • @metadata — decoded frontmatter map.
  • @route — route path such as /about/.
  • @site — discovered %Astral.Site{}.

Assets

Astral delegates assets to Volt. Reference source assets from layouts with Astral.asset_path/2:

<script type="module" src="<%= Astral.asset_path(@site, "app.ts") %>"></script>

In development this returns the source path served by Volt, for example /assets/app.ts. In static builds it reads Volt's manifest and returns the emitted file, for example /assets/app-5e6f7a8b.js.

Volt content hashes are enabled by default. For examples or prototypes that need stable filenames:

assets "assets" do
  entry "app.ts"
  url_prefix "/assets"
  hash false
end

Development server

mix astral.dev
mix astral.dev --open
mix astral.dev --config astral.config.exs --port 4000

The dev server:

  • serves Astral routes,
  • serves public files,
  • delegates Volt asset/HMR routes to Volt.DevServer,
  • injects Volt's HMR client into rendered HTML,
  • watches pages/layouts/public files for full reloads,
  • renders useful HTML error pages for Markdown/layout/config failures.

Static builds

mix astral.build

Example output:

[Astral] Built 2 page(s) into dist

Routes:
  /        dist/index.html
  /about/  dist/about/index.html

Assets:
  dist/assets/manifest.json

Upload dist/ to any static host or CDN. See guides/deployment.md for production asset behavior and deployment notes.

Example site

A runnable example lives in examples/basic:

cd examples/basic
mix deps.get
mix astral.dev
mix astral.build
mix check

It demonstrates Markdown, HTML pages, layouts, public files, Volt TypeScript/CSS assets, and Volt JS/TS formatting/linting.

Programmatic API

Astral.build(config: "astral.config.exs")
Astral.dev(config: "astral.config.exs", port: 4000)
Astral.asset_path(site, "app.ts")

Development

mix deps.get
mix ci

License

MIT © 2026 Danila Poyarkov