This is how it will look used with a layout:

  1. <%= sidebar %>

It generates:

  1. <aside id="sidebar">
  2. <div>hello</div>
  3. </aside>

Features

  • It knows how to close tags according to HTML5 spec (1)
  • It accepts content as first argument (2)
  • It accepts builder as first argument (3)
  • It accepts content as block which returns a string (4)
  • It accepts content as a block with nested markup builders (5)
  • It builds attributes from given hash (6)
  • it combines attributes and block (7)
  1. # 1
  2. html.div # => <div></div>
  3. html.img # => <img>
  4. # 2
  5. html.div('hello') # => <div>hello</div>
  6. # 3
  7. html.div(html.p('hello')) # => <div><p>hello</p></div>
  8. # 4
  9. html.div { 'hello' }
  10. # =>
  11. #<div>
  12. # hello
  13. #</div>
  14. html.div do
  15. p 'hello'
  16. end
  17. # =>
  18. #<div>
  19. # <p>hello</p>
  20. #</div>
  21. # 6
  22. html.div('hello', id: 'el', 'data-x': 'y') # => <div id="el" data-x="y">hello</div>
  23. # 7
  24. html.div(id: 'yay') { 'hello' }
  25. #<div id="yay">
  26. # hello
  27. #</div>

It supports complex markup constructs, without the need of concatenate tags. In the following example, there are two div tags that we don’t need link together.

The result is a very clean Ruby API.

The API is really simple: #tag must be used for a self-closing tag, where #empty_tag does the opposite.

  1. html.tag(:custom, 'Foo', id: 'next') # => <custom id="next">Foo</custom>
  2. html.empty_tag(:xr, id: 'next') # => <xr id="next">

The link_to helper, while appearing similar to tags generated by html, has different semantics and will not appear as part of the generator, as one might expect.

Since the return value of link_to is a string, it must be treated as such when combined with the generator offered by html.

Alone in a tag, the return value of link_to can be used as the content for the tag.

  1. html.div do
  2. link_to('hello', routes.root_path, class: 'btn')
  3. end
  4. # => <div>
  5. # => <a href="/" class="btn">hello</a>
  6. # => </div>
  1. html.div do
  2. link_to('Users', routes.users_path, class: 'btn') +
  3. link_to('Books', routes.books_path, class: 'btn')
  4. end

If you require multiple link_to inside the same tag, and there are other tags inside that tag, the return value of link_to will need to be passed to one of the html generator helpers.

This might be a block or inline tag such as div or span, but if no wrapping element is desired, the text helper can be used to render the link as-is.

The tag contents are automatically escaped for security reasons:

  1. html.div('hello') # => <div>hello</div>
  2. html.div { 'hello' } # => <div>hello</div>
  3. html.div(html.p('hello')) # => <div><p>hello</p></div>
  4. end # => <div><p>hello</p></div>
  5. html.div("<script>alert('xss')</script>")
  6. # => "<div>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>"
  7. html.div { "<script>alert('xss')</script>" }
  8. # => "<div>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>"
  9. html.div(html.p("<script>alert('xss')</script>"))
  10. # => "<div><p>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</p></div>"
  11. html.div do
  12. p "<script>alert('xss')</script>"
  13. end
  14. # => "<div><p>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</p></div>"

HTML attributes aren’t automatically escaped, in case we need to use a value that comes from a user input, we suggest to use #ha, which is the escape helper designed for this case. See for a deep explanation.

View Context

  1. module Web
  2. module Views
  3. module Books
  4. class Show
  5. include Web::View
  6. def title_widget
  7. html.div do
  8. h1 book.title
  9. end
  10. end
  11. end
  12. end
  13. end
  14. end
  1. <div id="content">
  2. </div>