laminar-form-derivation

December 9, 2025 ยท View on GitHub

Sonatype Central GitHub Workflow Status

This project derive UI Form for laminar with magnolia.

Docs

See Documentation.

Demo

For the very impatient, here is a live demo.

Installation

  • Scala LTS: v0.24.3
  • Scala > 3.6.5: v1.0.0+
// With raw Laminar widgets (html only)
libraryDependencies += "dev.cheleb" %%% "laminar-form-derivation-ui" % "0.11.0"
// With UI5 Web Components
libraryDependencies += "dev.cheleb" %%% "laminar-form-derivation-ui5" % "0.11.0"

Run the example

Client side is reloaded in dev mode with vite, server side is built with sbt

Client code is in example/client

Development

VSCode with metals

Prerequisites

Just open the project with vscode and enjoy the magic

code .

As soon as you open the project, you will be prompted to import the build, click on the "Import build" button.

Import build

Then wait a few seconds for the build to import ...

You will have the following tasks:

  • sbt fastLink client
  • vite dev hot reloading

Tasks

Manual

With vite hot reload

  • Teminal 1
sbt clean
DEV=1 sbt "~client/fastLinkJS"
  • Terminal 2
 cd example/client
 npm install
 npm run dev

Open http://localhost:3000, changes are hot reloaded in the browser when you save the HelloWorld.scala.

Production mode

With a ZIO http server (work in progress)

sbt server/run  

Open http://localhost:8080

With server restart on change

sbt "~server/reStart"

Reload the page to see the changes...

Sample Usage

package demo

import dev.cheleb.scalamigen.*
import dev.cheleb.scalamigen.Form.given
import org.scalajs.dom
import com.raquo.laminar.api.L.*

// Define some models
case class Person(
    name: String,
    fav: Pet,
    pet: Option[Pet],
    email: Option[String],
    age: Int
)
case class Pet(name: String, age: Int, House: House, size: Option[Int])

case class House(capacity: Int)

// Provide default for optional
given Defaultable[Pet] with
  def default = Pet("No pet", 0, House(0), None)

// Instance your model
val agnes =
  Person(
    "Vlad",
    Pet("Batman", 666, House(2), Some(169)),
    Some(Pet("Wolfy", 12, House(1), Some(42))),
    Some("vlad.dracul@gmail.com"),
    48
  )

val itemVar = Var(agnes)

object App extends App {

  val myApp =
    div(
      child <-- itemVar.signal.map { item =>
        div(
          s"$item"
        )
      },
      Form.renderVar(itemVar)
    )

  val containerNode = dom.document.getElementById("root")
  render(containerNode, myApp)
}

Renders the following form:

Form

More info in the example