{ title: “HTML Generation” }

HTML Generation

This guide is focused on using builder syntax in Lua/MoonScript to generate HTML. If you’re interested in a more traditional templating system see the etlua Templates guide.

HTML Builder Syntax

HTML templates can be written directly as MoonScript (or Lua) code. This is a very powerful feature (inspired by Erector) that gives us the ability to write templates with high composability and also all the features of MoonScript or Lua. No need to learn any goofy templating syntax with arbitrary restrictions.

In the context of a HTML renderer, the environment exposes functions that create HTML tags. The tag builder functions are generated on the fly as you call them. The output of these functions is written into a buffer that is compiled in the end and returned as the result

Here are some examples of the HTML generation:

div!                -- <div></div>
b "Hello World"     -- <b>Hello World</b>
div "hi<br/>"       -- <div>hi&lt;br/&gt;</div>
text "Hi!"          -- Hi!
raw "<br/>"         -- <br/>

element "table", width: "100%", ->  -- <table width="100%"></table>

div class: "footer", "The Foot"     -- <div class="footer">The Foot</div>

input required: true                -- <input required/>

div ->                              -- <div>Hey</div>
  text "Hey"

div class: "header", ->             -- <div class="header"><h2>My Site</h2>
  h2 "My Site"                      --    <p>Welcome!</p></div>
  p "Welcome!"

The element function is a special builder that takes the name of tag to generate as the first argument followed by any attributes and content.

The HTML builder methods have lower precedence than any existing variables, so if you have a variable named div and you want to make a <div> tag you’ll need to call element "div".

If you want to create a <table> or <select> tag you’ll need to use element because Lua uses those names in the built-in modules.

All strings passed to the HTML builder functions (attribute names, values, or tag contents) are escaped automatically. You never have to worry about introducing any cross site scripting vulnerabilities.

Special attributes

The class attribute can be passed as a table, and the class list will be constructed from it. The table can contain either array element, or hash elements:

div {
  class: {"one", "two", three: false, four: true}
}, "Hello world!"

Will generate:

<div class="one two four">Hello world!</div>

Helper functions

In addition to the tag functions, a few other helper functions are also available:

  • raw(str) – outputs the argument, a string, directly to the buffer without escaping.
  • capture(func) – executes the function argument in the context of the HTML builder environment, returns the compiled result as a string instead of writing to buffer.
  • text(args) – outputs the argument to the buffer, escaping it if it’s a string. If it’s a function, it executes the function in HTML builder environment. If it’s a table, it writes each item in the table
  • widget(SomeWidget) – renders another widget in the current output buffer. Automatically passes the enclosing context
  • render(template_name) – renders another widget or view by the module name. Lets you render etlua templates from inside builder

HTML In Actions

If we want to generate HTML directly in our action we can use the @html method:

"/": =>
  @html ->
    h1 class: "header", "Hello"
    div class: "body", ->
      text "Welcome to my site!"

The environment of the function passed to @html is set to one that support the HTML builder functions described above. The return value of the @html method is the generated HTML as a string. Returning this from the action allows us to render send it right to the browser

HTML Widgets

The preferred way to write HTML is through widgets. Widgets are classes who are only concerned with outputting HTML. They use the same syntax as the @html helper shown above for writing HTML.

When Lapis loads a widget automatically it does it by package name. For example, if it was loading the widget for the name "index" it would try to load the module views.index, and the result of that module should be the widget.

This is what a widget looks like:

-- views/index.moon
import Widget from require "lapis.html"

class Index extends Widget
  content: =>
    h1 class: "header", "Hello"
    div class: "body", ->
      text "Welcome to my site!"

The name of the widget class is insignificant, but it’s worth making one because some systems can auto-generate encapsulating HTML named after the class.

Rendering A Widget From An Action

The render option key is used to render a widget. For example you can render the "index" widget from our action by returning a table with render set to the name of the widget:

"/": =>
  render: "index"

If the action has a name, then we can set render to true to load the widget with the same name as the action:

[index: "/"]: =>
  render: true

By default views. is prepended to the widget name and then loaded using Lua’s require function. The views prefix can be customized by overwriting the views_prefix member of your application subclass:

class Application extends lapis.Application
  views_prefix: "app_views"

  -- will use "app_views.home" as the view
  [home: "/home"]: => render: true

Passing Data To A Widget

Any @ variables set in the action can be accessed in the widget. Additionally any of the helper functions like @url_for are also accessible.

-- app.moon
class App extends lapis.Application
  [index: "/"]: =>
    @page_title = "Welcome To My Page"
    render: true
-- views/index.moon
import Widget from require "lapis.html"

class Index extends Widget
  content: =>
    h1 class: "header", @page_title
    div class: "body", ->
      text "Welcome to my site!"

Rendering Widgets Manually

Widgets can also be rendered manually by instantiating them and calling the render_to_string method.

Index = require "views.index"

widget = Index page_title: "Hello World"
print widget\render_to_string!

If you want to use helpers like @url_for you also need to include them in the widget instance. Any object can be included as a helper, and its methods will be made available inside of the widget.

html = require "lapis.html"
class SomeWidget extends html.Widget
  content: =>
    a href: @url_for("test"), "Test Page"

class extends lapis.Application
  [test: "/test_render"]: =>
    widget = SomeWidget!
    widget\include_helper @

You should avoid rendering widgets manually when possible. When in an action use the render request option. When in another widget use the widget helper function. Both of these methods will ensure the same output buffer is shared to avoid unnecessary string concatenations.


Whenever an action is rendered normally the result is inserted into the current layout. The layout is just another widget, but it is used across many pages. Typically this is where you would put your <html> and <head> tags.

Lapis comes with a default layout that looks like this:

html = require "lapis.html"

class DefaultLayout extends html.Widget
  content: =>
    html_5 ->
      head -> title @title or "Lapis Page"
      body -> @content_for "inner"

Use this as a starting point for creating your own layout. The content of your page will be injected in the location of the call to @content_for "inner".

We can specify the layout for an entire application or specify it for a specific action. For example, if we have our new layout in views/my_layout.moon

class extends lapis.Application
  layout: require "views.my_layout"

If we want to set the layout for a specific action we can provide it as part of the action’s return value.

class extends lapis.Application
  -- the following two have the same effect
  "/home1": =>
    layout: "my_layout"

  "/home2": =>
    layout: require "views.my_layout"

  -- this doesn't use a layout at all
  "/no_layout": =>
    layout: false, "No layout rendered!"

As demonstrated in the example, passing false will prevent any layout from being rendered.

Widget Methods


Class method that copies the methods from another class into this widget. Useful for mixin in shared functionality across multiple widgets.

class MyHelpers
  item_list: (items) =>
    ul ->
      for item in *items
        li item

class SomeWidget extends html.Widget
  @include MyHelpers

  content: =>
    @item_list {"hello", "world"}

@content_for(name, [content])

content_for is used for sending HTML or strings from the view to the layout. You’ve probably already seen @content_for "inner" if you’ve looked at layouts. By default the content of the view is placed in the content block called "inner".

You can create arbitrary content blocks from the view by calling @content_for with a name and some content:

class MyView extends Widget
  content: =>
    @content_for "title", "This is the title of my page!"

    @content_for "footer", ->
      div class: "custom_footer", "The Footer"

You can use either strings or builder functions as the content.

To access the content from the layout, call @content_for without the content argument:

class MyLayout extends Widget
  content: =>
    html ->
      body ->
        div class: "title", ->
          @content_for "title"

        @content_for "inner"
        @content_for "footer"

If a string is used as the value of a content block then it will be escaped before written to the buffer. If you want to insert a raw string then you can use a builder function in conjunction with the raw function:

@content_for "footer", ->
  raw "<pre>this wont' be escaped</pre>"


Checks to see if content for name is set.

class MyView extends Widget
  content: =>
    if @has_content_for "things"
      @content_for "things"
      div ->
        text "default content"

HTML Module

html = require "lapis.html"


Runs the function, fn in the HTML rendering context as described above. Returns the resulting HTML. The HTML context will automatically convert any reference to an undefined global variable into a function that will render the appropriate tag.

import render_html from require "lapis.html"

print render_html ->
  div class: "item", ->
    strong "Hello!"


Escapes any HTML special characters in the string. The following are escaped:

  • &&amp;
  • <&lt;
  • >&gt;
  • "&quot;
  • '&#039;