Section Components
The section component pattern
Define section renderers
# lib/my_app_web/components/sections.ex
defmodule MyAppWeb.Sections do
use Phoenix.Component
# Function name matches the section template name
def hero(assigns) do
~H"""
<section class="hero">
<h1>{@fields["heading"]}</h1>
<p>{@fields["subheading"]}</p>
</section>
"""
end
def richtext(assigns) do
~H"""
<section class="prose">
{raw(@fields["body"])}
</section>
"""
end
endAccess field values
# String fields
@fields["heading"]
# Richtext (HTML string, use raw/1)
raw(@fields["body"])
# Media (resolve slug to URL)
Brix.Render.media_url(@fields["image"])
# List fields
for item <- @fields["items"] do
item["label"]
end
# Boolean fields
if @fields["visible"] do
# ...
endRendering Sections & Layouts
Using Brix.Render components
Render page sections
<%!-- Dispatches each section to the matching
function in MyAppWeb.Sections --%>
<Brix.Render.sections
sections={@page.sections}
module={MyAppWeb.Sections}
/>Each section's template field is used to call the corresponding function component in the given module.
Render a layout with content
<%!-- Renders header sections, inner block,
then footer sections --%>
<Brix.Render.layout
layout={@layout}
module={MyAppWeb.Sections}
>
<%!-- Page content goes here --%>
<Brix.Render.sections
sections={@page.sections}
module={MyAppWeb.Sections}
/>
</Brix.Render.layout>Resolve media URLs
# In templates
<img src={Brix.Render.media_url("hero-image")} />
# Returns "/content/media/{path}" or ""
Brix.Render.media_url("hero-image")SEO in Root Layout
Using Brix.Meta.field/3 for meta tags
Root layout meta tags
<%!-- root.html.heex --%>
<head>
<title>{Brix.Meta.field(@page, @site, :title)}</title>
<meta
name="description"
content={Brix.Meta.field(@page, @site, :description)}
/>
<meta
property="og:title"
content={Brix.Meta.field(@page, @site, :og_title)}
/>
<meta
property="og:description"
content={Brix.Meta.field(@page, @site, :og_description)}
/>
<meta
property="og:image"
content={Brix.Meta.field(@page, @site, :og_image)}
/>
</head>Available meta fields
| Atom | Fallback chain |
|---|---|
:title | page.meta_title -> page.title -> site.meta_title -> site.name |
:description | page.meta_description -> site.meta_description |
:og_title | page.og_title -> :title fallback |
:og_description | page.og_description -> :description fallback |
:og_image | page.og_image -> site.og_image |
Works with both %Page{} and %Collection{} structs.
Slug Redirects
Handling old URLs
In a LiveView or controller
def handle_params(%{"path" => path}, _uri, socket) do
slug = "/" <> Enum.join(path, "/")
case Brix.get_page(slug) do
{:ok, page} ->
{:noreply, assign(socket, page: page)}
:error ->
case Brix.find_redirect(slug) do
{:ok, new_slug} ->
{:noreply,
push_navigate(socket, to: new_slug)}
:error ->
raise MeWeb.NotFoundError
end
end
endDefine slug history in page.yml
# pages/blog/new-post/page.yml
title: My Post
slug_history:
- /blog/old-post
- /posts/my-postOld slugs automatically resolve via Brix.find_redirect/1.
Store Setup
Configuring Brix.Store.Filesystem
application.ex
# lib/my_app/application.ex
def start(_type, _args) do
children = [
{Brix.Store.Filesystem,
content_dir:
Path.join(:code.priv_dir(:my_app), "content")},
# ... other children
]
opts = [strategy: :one_for_one, name: MyApp.Supervisor]
Supervisor.start_link(children, opts)
endRuntime reload
# Reload content after editing files
Brix.reload()
# Validate content before loading
Brix.Validator.validate(content_dir)
#=> %{errors: [], warnings: []}On boot, content is validated automatically. Errors block loading; warnings are logged.