Brew components
A Brew component is a subclass of org.konaframework.brew.Component. Public fields (or Kotlin properties with backing fields) are state. Public methods are actions. The class returns HTML through template(), render(), or a convention Crema view.
Two authoring styles
Section titled “Two authoring styles”Single-file (inline template)
Section titled “Single-file (inline template)”Override template() with Crema source. Best for small widgets.
class Counter : Component() { var count = 0 fun increment() { count++ }
override fun template() = """ <div> <button wire:click="increment">+</button> <span>{{ count }}</span> </div> """.trimIndent()}Class + Crema view
Section titled “Class + Crema view”Omit template() and either return view("brew.todos") from render(), or rely on the convention path views/brew/<name>.crema.html.
class Todos : Component() { var draft = "" var items = mutableListOf<String>()
fun add() { if (draft.isNotBlank()) { items.add(draft.trim()) draft = "" } }
// Convention: views/brew/todos.crema.html when render() / template() are null}public class Todos extends Component { public String draft = ""; public List<String> items = new ArrayList<>();
public void add() { if (!draft.isBlank()) { items.add(draft.trim()); draft = ""; } }}{{-- views/brew/todos.crema.html — single root element required --}}<div> <form wire:submit="add"> <input wire:model="draft" /> <button type="submit">Add</button> </form> <ul> @foreach(items as item) <li wire:key="todo-{{ loop.index }}">{{ item }}</li> @endforeach </ul></div>render() may return:
| Return | Meaning |
|---|---|
null |
Use template(), else convention view brew.<name> |
BrewView from view(name, data) / inline(source, data) |
Named or inline Crema |
String |
Raw HTML (still must have a single root element) |
Naming and discovery
Section titled “Naming and discovery”By default BrewServiceProvider adds the app.brew package location. Class app.brew.Counter is named counter; nested packages become dotted names (app.brew.posts.CreatePost → posts.create-post).
| API | Role |
|---|---|
Brew.discover("app.brew") |
Scan a package |
Brew.addLocation("com.acme.ui") |
Extra package root |
Brew.addNamespace("admin", "app.admin.brew") |
Resolve admin::users |
Brew.component("counter", Counter::class.java) |
Explicit registration |
@BrewComponent("explicit-name") |
Pin the discovered name |
Namespaces use ns::name in tags: <brew:admin::users />.
Embedding
Section titled “Embedding”<brew:counter /><brew:counter :start="3" title="Demo" />@brew('counter', ['start' => 3])- Static attributes become string props / mount args.
:attr="expr"evaluates the Crema expression.- Kebab-case attributes map to camelCase (
:user-id→userId).
From code:
Brew.mount("counter", mapOf("start" to 3))Brew.exists("counter") // true when registered / discoverableIdentity helpers
Section titled “Identity helpers”Inside a running component:
id() // unique instance idname() // registered name, e.g. "counter"Errors you will see
Section titled “Errors you will see”| Situation | Exception / behaviour |
|---|---|
| Unknown name | ComponentNotFoundException |
| More than one root HTML element | MultipleRootElementsException |
| Empty / no root tag | RootTagMissingFromViewException |
| Client calls a non-action / lifecycle method | Method-not-found / lifecycle guard |
Scaffolding
Section titled “Scaffolding”./grind make:brew counter # single-file in app.brew./grind make:brew posts.create --class # class + views/brew/posts/create.crema.html./grind make:brew counter --lang=java./grind make:brew counter --test- Properties — state API and security annotations
- Actions — calling methods from the browser
- Templates — assets and Crema surface