Reclaim control of your HTML
Creating a basic HTML structure in j2html is pretty similar to plain HTML. This Java code:
html( head( title("Title"), link().withRel("stylesheet").withHref("/css/main.css") ), body( main(attrs("#main.content"), h1("Heading!") ) ) )
html().with( head().with( title("Title"), link().withRel("stylesheet").withHref("/css/main.css") ), body().with( main().withId("main").withClass("content").with( h1("Heading!") ) ) )
It’s literally impossible to forget to close a div, mistype an attribute name, or forget an attribute quote! Remember to include the Java wrapping code though, j2html is not a template language, all files are .java. To see how the wrapping code could look, check out the getting started example.
Core concepts
TagCreator.class // Static utility class for creating all tags import static j2html.TagCreator.*; // Use static star import Config.class // Holds all configuration. Offers global configuration or customizable instances Config.closeEmptyTags = true // Global options are public, static and mutable. Config.global() // Copy all static Config fields into an instance. Instances are immutable Config.defaults() // A Config with defaults that are independent of global options Config.global().withEmptyTagsClosed(true) // A Config that is different from the global options Config.defaults().withEmptyTagsClosed(true) // A Config that is different from the default options TagCreator.join() // Method for joining small snippets, like: p(join("This paragraph has", b("bold"), "and", i("italic"), "text.")) TagCreator.iff(boolean condition, T ifValue) // If-expression for use in method calls div().withClasses("menu-element", iff(isActive, "active")) TagCreator.iffElse(boolean condition, T ifValue, T elseValue) // If/else-expression for use in method calls div().withClasses("menu-element", iffElse(isActive, "active", "not-active")) Tag.class // Is extended by ContainerTag (ex and EmptyTag (ex
) Tag.attr(String attribute, Object value) // Set an attribute on the tag Tag.withXyz(String value) // Calls attr with predefined attribute (ex .withId, .withClass, etc.) Tag.render(HtmlBuilder builder) // Render HTML using the given builder. Tag.render() // Shortcut for rendering flat HTML into a string using global Config. ContainerTag.renderFormatted() // Shortcut for rendering indented HTML into a string using global Config. HtmlBuilder.class // Interface for composing HTML. Implemented by FlatHtml and IndentedHtml FlatHtml.into(Appendable) // Render into a stream, file, etc. without indentation or line breaks FlatHtml.into(Appendable appendable, Config config) // Customize rendering of flat html IndentedHtml.into(Appendable) // Render human-readable HTML into an stream, file, etc. IndentedHtml.into(Appendable appendable, Config config) // Customize rendering of intended html ul(li("one"), li("two")).render(IndentedHtml.inMemory()).toString() // Similar to renderFormatted() ul(li("one"), li("two")).render(IndentedHtml.into(filewriter)) // Write HTML into a file
Loops, each() and filter()
Using Java 8’s lambda syntax, you can write loops (via streams) inside your HTML-builder:
body( div(attrs("#employees"), employees.stream().map(employee -> div(attrs(".employee"), h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ).toArray(ContainerTag[]::new) ) )
body().with( div().withId("employees").with( employees.stream().map(employee -> div().withClass("employee").with( h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ).toArray(ContainerTag[]::new) ) )
j2html also offers a custom each method, which is slightly more powerful:
// each() lets you iterate through a collection and returns the generated HTML // as a DomContent object, meaning you can add siblings, which is not possible // using the stream api in the previous example body( div(attrs("#employees"), p("Some sibling element"), each(employees, employee -> div(attrs(".employee"), h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ) ) )
// each() lets you iterate through a collection and returns the generated HTML // as a DomContent object, meaning you can add siblings, which is not possible // using the stream api in the previous example body().with( div().withId("employees").with( p("Some sibling element"), each(employees, employee -> div().withClass("employee").with( h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ) ) )
If you need to filter your collection, j2html has a built in filter function too:
// filter() is meant to be used with each(). It just calls the normal // stream().filter() method, but hides all the boilerplate Java code // to keep your j2html code neat body( div(attrs("#employees"), p("Some sibling element"), each(filter(employees, employee -> employee != null), employee -> div(attrs(".employee"), h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ) ) )
// filter() is meant to be used with each(). It just calls the normal // stream().filter() method, but hides all the boilerplate Java code // to keep your j2html code neat body().with( div().withId("employees").with( p("Some sibling element"), each(filter(employees, employee -> employee != null), employee -> div().withClass("employee").with( h2(employee.getName()), img().withSrc(employee.getImgPath()), p(employee.getTitle()) ) ) ) )
Since this is pure Java, all the Employee methods (getName, getImgPath, getTitle) are available to you, and you get autocomplete suggestions and compile time errors.
Given three random employees, all the above approaches would give the same HTML:
David Creator of Bad Libraries
Christian Fanboi of Jenkins
Paul Hater of Lambda Expressions