Docs / Websites

Luat: templates and blocks

The files below are from examples/site-luat in the Studio repository.

The page

load_page returns the page’s props; the page template renders its content with the package’s Blocks component:

<!-- src/lib/Page.luat — used by "/" and the catch-all route -->
<script>
  local Blocks = require("@maravilla/studio/Blocks")
  setContext("studio", props.studio or {})               -- editing state, locale, visitor, media URLs
  setContext("studio.components", require("$lib/blocks")) -- your own blocks (optional)
</script>
{#if props.connecting}
  <p class="notice">Connecting to the editor…</p>
{:else}
  <article>
    <h1 data-studio-doc-field="title">{props.title}</h1>
    <div data-studio-section="root" data-studio-section-name="content">
      <Blocks blocks={props.content or {}} />
    </div>
  </article>
{/if}

data-studio-doc-field makes the page’s own title editable, and data-studio-section turns the content list into the editor’s drop target — the same annotations every Studio site uses. props.connecting is the brief state while the editor opens the page.

The layout

The layout’s load builds the chrome around every page — the story’s header and footer pages, the navigation, the language switch — and the editor mount:

-- src/routes/+layout.server.lua (abridged)
local studio = require("@maravilla/studio")

function load(ctx)
  local s = studio.new()
  local rc = s:context(ctx)
  s:send_headers(ctx, rc)                -- framing for the editor, noindex for previews
  local chrome = s:chrome(rc)            -- story, header and footer (with drafts), nav
  local visitor = s:visitor(rc, ctx)
  return {
    chrome = chrome,
    lang = rc.locale,
    head = table.concat({
      require("@maravilla/studio/styles").tag(),   -- optional default block CSS
      studio.editor.head(rc, { regions = { "#studio-page", "#site-header", "#site-footer" } }),
    }, "\n"),
    layout_studio = s:template_context(rc, visitor),
  }
end
<!-- src/routes/+layout.luat (abridged) -->
<html lang={props.lang or "en"}>
<head>
  <title>{props.title}</title>
  {@html props.head or ""}
</head>
<body>
  <header id="site-header" data-studio-ignore>…</header>
  <main id="studio-page" data-studio-root>{@html props.children}</main>
  <footer id="site-footer" data-studio-ignore>…</footer>
</body>
</html>

src/app.html is reduced to <!DOCTYPE html>%luat.body%: the layout renders the whole document, because the language, title and editor mount change per request. editor.head renders the editor mount only for requests from the editor; the regions are what the editor swaps in place after an edit. Header and footer are data-studio-ignore: they are pages of their own and are edited there.

The standard blocks

require("@maravilla/studio/<Name>"):

ComponentBlock
Blocks, Elementa content list / one block, through the registry
Herostandard:Hero
TextBlockstandard:Prose
Headingstandard:Heading
ImageBlockstandard:Figure
FeatureGridstandard:FeatureGrid
Sectionstandard:Section (a nested drop container)
Variants, StudioVariantsstandard:Variants / standard:Switcher
Fallbackanything not registered — shown, never silently dropped
RichText<RichText value={props.body} as="div" field="body" />

Images and files render through the stable /studio-media/… route, so a page never embeds a URL that expires.

Your own blocks

A block component receives the block’s fields as props (props.uuid, props.element_type and the block’s own fields) and annotates itself so the editor can select and edit it:

<!-- src/lib/blocks/Pullquote.luat -->
<script>
  local studio = require("@maravilla/studio")
</script>
<blockquote class="pullquote" data-studio-id={props.uuid} data-studio-type={props.element_type}>
  <p data-studio-field="quote">{props.quote or ""}</p>
  {#if studio.util.present(props.attribution)}
    <cite data-studio-field="attribution">{props.attribution}</cite>
  {/if}
</blockquote>

Register it by element type — a site entry wins over the package’s component, so you can also replace a standard block:

-- src/lib/blocks.lua
return {
  ["standard:Pullquote"] = require("$lib/blocks/Pullquote"),
}

studio.editor has helpers that build the same attributes as tables (editor.block(el), editor.field(name, { kind = "rich" }), editor.section(owner, name)) for components that assemble attributes in code.

Pilot documentation. Studio is in a partner pilot. Screens and YAML shown here are current as of the version noted on each page; tell your Maravilla contact when something does not match what you see.