The ScalaTags is an HTML/XML/CSS construction library for Scala. By using Scala, you have the benefits of strong type (IDE support of syntax help and code completion). Another huge saving is that you don’t need to learn a second template pseudo-language. Scalatags is two times faster than Twirl.

Introduction

The library is ivy"com.lihaoyi::scalatags:0.9.1". Use import scalatags.Text.all._ to import values and methods.

The core concept is to generate fragment using plain Scala. This is done by tag values such as head, script, and so on. Scalatags fragments are just normal Scala values that can be shared, stored, and more importantly, rendered into HTML.

To start, use "<!DOCTYPE html>" + html(...). Other tags are head, body, h1, div, p, a, and so on. Use tag function to create custom tag such as tag("rss"). The html is actually tag("html").

Attributes and Styles

Each attribute has an associated value that is used to set it. To set an attribute, use := operator. For example: p(onclick := ..., ) or a(href := "google.com"). Attributes are all instances of the Attr class. Multiple attribues are separated by ,, for example, h1(backgroundColor:="blue", color:="red")("This is my title").

Data attribute name has multiple parts separated by ., instead of -.

HTML style attributes are a list of key-value pairs. There is a large but finite number of sytles and style values. Scalatags comes with style values such as backgroundColor := "blue", color := "red", cls := "p-2 m2", opacity := 0.9 and etc. cls is class. They are instances of the Style class. You can also use style:="background-color: blue; color: red;" directly.

You can define customized attributes and styles. For example, attr("href") := "google.com, attr("data-app-key") := "MyKey" or attr("hidden").empty for an empty attribute. Use css("color") := "red" for styles. The name and value can be separated:

1
2
3
4
5
6
val dataAppKey = attr("data-app-key")
val customBackgroundColorStyle = css("background-color")
div(
  dataAppKey := "YOUR_APP_KEY",
  customBackgroundColorStyle := "red"
)

Some are Enums, Ints or Booleans. For example float.left, tabIndex := 10, disable := true. The input(readonly) becomes <input readlony="readonly">.

You can define inline modifiers and cascading styles.

Variable, Control Flow and Functions

Variables can be added to an elemnt’s children, as a separate parameter. Control flows such as if and for can be used as regaluar Scala code. So are functions.

By default, all string values are escapted in Scalatags tempaltes. To disable it, use raw(myString).

To define layouts, use Seq[Modifer] as parameters in a function. Modifer is an attribute or a style.

Another way to define layout is to defain a class as an paraent, then define an object child.

Use Seq[Frag] or frag to group fragments as a sequence or a single fragment. Use Seq[Modifier] to group attributes or styles or modifier() for group some into a single Modifier.

Manage Imports

The scalatags.Text namespace has the following packages:

  • all: all except less-used tags2, styles2 and svg values.
  • short: it import tags, attrs, styles and DataConverters, but aliases Attrs and Styles as * to not put styles into global namespace. For example, p(*.color := "red").
  • tags: commont tags
  • tags2: less common tags
  • attrs: common attributes
  • styles: common styles
  • styles2: less common styles
  • svgTags: SVG tags
  • svgAttrs: SVG tag attributes
  • DataConverters: convert extnesions such as 10.px.
  • implicits: it combines Aggregate and DataConverters. Aggregate has implicit converts for StringFrag, StyleFrag and Modifier.

Use import to define shortcust such as js(s: String) for script(src := s).

You can define a custome bulding using a syntax like object MyBundle extends Text.Cap with Text.Tags.

Internals

A Frag is the smallest standalone constructor that represents a renderable snippet that contains zero or more HTML tags, string nodes, numbers and other things.

A Modifier represents anything that can be applied to a Tag: styles, classes, attributes, as well as any Frag that can be appended to its children. It could be a tag, a sequence of tags, an attribute bindings, a style binding, or anything that can be used to modify how a tag will be rendered.

A TypedTag is a tag-name together with a bag of Modifiers, and is itself a Modifier so it can be nested within other TypedTags. For HTML rendering, the Tag is defined as TypedTag[Builder, String, String] and the Builder is text.Builder.

The current selection of Modifiers include:

  • TypedTags and String: appends itself to the parent’s children list.
  • AttrPair: set a key in the parent’s attrs list.
  • StylePair: appends the inline style: value to the parent
  • StringFrag: append the string to parent.
  • RawFrag: append without escaping.

For simplification, in text bundle, the types can be thought as:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
trait Modifier {
  def applyTo(t: Builder): Unit
}

trait Frag extends Modifier {
  def render: String
}

trait TypedTag extends Frag {
  def tag: String
  def modifiers: List[Seq[Modifier]]
  def apply(xs: Modifier*): Self
  def render: String
}

The actual code is below

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
trait Modifier[Builder] {
  def applyTo(t: Builder): Unit
}

trait Frag[Builder, FragT] extends Modifier[Builder]{
  def render: FragT
}

// Output <: FragT. Output is the final output type of a TypedTag
// FragT is the type generated by each Frag
trait TypedTag[Builder, +Output, FragT] extends Frag[Builder, FragT]{
  def tag: String
  def modifiers: List[Seq[Modifier[Builder]]]
  def apply(xs: Modifier[Builder]*): Self
  def render: Output
}

Commmon Patterns

  • Use cls for class, tpe for type.
  • The head’s title attribute is scalatags.Text.tags2.title.
  • Use attr("data-todo-id") := "15" or css("background-color") := "red" for hyphen-separated name.
  • Use Frag as parameter to take a single tag or frag(tag1, tag2, ...) for multiple tags.