All frameworks
ANJUNAR / OPEN SOURCE

Scala.js UI

A Scala 3 and Scala.js UI framework for server-rendered applications.

01

The idea

Build components in Scala, render their initial HTML on the server, then hydrate the same component tree in the browser. The framework combines reactive state, routing, forms, controls, and lifecycle-aware rendering.

02

What it does.

01

Server-side rendering

Render crawlable HTML from the same components used by the browser.

02

Hydration

Attach behavior to server-rendered markup without rebuilding the page.

03

Component library

Use a Scala.js DSL, routing, forms, and controls in one runtime.

03

Installation

Add to your project
libraryDependencies += "com.anjunar" %% "scalajs-ui-core" % "1.0.6"

Package: com.anjunar:scalajs-ui-core

04

Start building.

Enable ScalaJSPlugin, add scalajs-ui-core, and mount a component with Runtime.mount.

A reactive component

One property drives the displayed count; the button updates it without rebuilding the page.

Source code
import ui.core.component.AbstractComponent
import ui.core.dsl.ClassDsl.classes
import ui.core.dsl.EventDsl.onClick
import ui.core.layout.Button.button
import ui.core.layout.Div.div
import ui.core.layout.TextComponent.text
import ui.core.layout.VBox.vbox
import ui.core.render.Cursor
import ui.core.state.Property

final class Counter extends AbstractComponent {
  val tagName = "main"
  override def compose(cursor: Cursor): Unit = {
    val count = Property(0)
    vbox {
      classes = Seq("counter")
      div { text(count.map(n => s"Count: $n")) {} }
      button("Increment") { onClick(_ => count.set(count.get + 1)) }
    }
  }
}

Render on the server, hydrate in the browser

The same component is mounted into an SSR cursor and then claimed by a hydration cursor.

Source code
import ui.core.async.AsyncRenderContext
import ui.core.component.Runtime
import ui.core.render.HydratingCursor
import org.scalajs.dom

val html = Runtime.renderToStringAsync { cursor =>
  Runtime.mount(new Counter, cursor)
}

val async = new AsyncRenderContext()
val cursor = HydratingCursor.root(dom.document, async)
Runtime.mount(new Counter, cursor)
05

By design.

A single component tree drives server rendering, browser hydration, and reactive updates. Component disposal owns subscriptions and event listeners.

Your next step

Make it part of your stack.

Built carefully with Scala JS UI.Imprint