One Scala 3 library for the whole stack. Uni gives you object wiring (Design), logging, JSON/MessagePack, Rx, HTTP client/server, RPC, MCP servers, and testing from one project, with minimal dependencies, and the same code runs on JVM, Scala.js, and Scala Native. It is the Scala 3 successor of Airframe.
For AI coding agents: read llms.txt (page index) or llms-full.txt (all docs in one file), and see Uni for AI Agents for canonical imports and an Airframe migration table.
Already using the Scala Toolkit? See Uni and the Scala Toolkit for when each fits and how they work together.
Runnable example apps, compiled and tested in CI, live in examples/: a CLI tool
(JVM and Scala Native), a REST + RPC server, a stdio MCP server, and Scala.js UIs. Copy one as a
starting point.
Claude Code users can install the Uni skill, which teaches the agent idiomatic Uni code:
/plugin marketplace add wvlet/uni
/plugin install uni@uni
Uni is the Scala 3 successor of Airframe. The two projects have different goals:
- Uni supports only Scala 3. Dropping Scala 2 lets it make full use of Scala 3 features in its APIs and implementation. It also bundles the core utilities into a few modules (
uni,uni-netty,uni-test) instead of manyairframe-*artifacts. - Airframe supports both Scala 2 and Scala 3, and it remains maintained. There is no plan to deprecate it.
For a new Scala 3 project, use Uni. If your project still needs Scala 2, or you depend on Airframe modules that Uni doesn't cover, keep using Airframe. See the Airframe migration table for how APIs map between them.
| Module | Description |
|---|---|
uni |
Core utilities: Design (object wiring), logging, JSON, MessagePack, Rx, HTTP client, control flow |
uni-netty |
Netty-based HTTP server (JVM only) |
uni-test |
Lightweight testing framework with cross-platform support |
uni and uni-test are cross-platform: JVM, Scala.js, and Scala Native.
Add the dependencies you need to your build.sbt (replace <version>
with the version shown in the Maven Central badge above):
// Core utilities (Design, logging, JSON, MessagePack, Rx, HTTP client, etc.)
libraryDependencies += "org.wvlet.uni" %% "uni" % "<version>"
// Netty-based HTTP server (JVM only; pulls in uni transitively)
libraryDependencies += "org.wvlet.uni" %% "uni-netty" % "<version>"
// Testing framework
libraryDependencies += "org.wvlet.uni" %% "uni-test" % "<version>" % Test
testFrameworks += new TestFramework("wvlet.uni.test.Framework")On sbt 1.x, use %%% for uni and uni-test in Scala.js or Scala
Native projects (sbt 2.x uses %% for all platforms):
libraryDependencies += "org.wvlet.uni" %%% "uni" % "<version>"
libraryDependencies += "org.wvlet.uni" %%% "uni-test" % "<version>" % TestA minimal example using the logging API:
import wvlet.uni.log.LogSupport
class MyService extends LogSupport:
def greet(name: String): String =
info(s"Greeting ${name}")
s"Hello, ${name}!"
@main def hello =
val service = MyService()
println(service.greet("World"))See the reference docs for Design, Rx, JSON/MessagePack, HTTP, and more.
uni-netty provides a Netty-based HTTP server:
import wvlet.uni.http.{Request, Response}
import wvlet.uni.http.netty.NettyServer
import wvlet.uni.rx.Rx
NettyServer
.withPort(8080)
.withRxHandler { request =>
Rx.single(Response.ok(s"Hello from ${request.path}"))
}
.start { server =>
println(s"Listening on ${server.localAddress}")
}See the REST Server guide for the
annotation-based router, filters, and the Scala.js / Scala Native
server backends (which need only uni).
Write a test by extending UniTest:
import wvlet.uni.test.UniTest
class MyTest extends UniTest:
test("addition works") {
(1 + 1) shouldBe 2
}Run with sbt test. See the UniTest guide
for assertions, property-based testing, and more.
Uni also ships sbt plugins for sbt 2.x, usable in any sbt 2.x project (with or without the uni library):
| Plugin | Description |
|---|---|
sbt-uni |
Type-safe HTTP/RPC client generation from service traits, plus ~uniRestart — background fork-run on every code change (an sbt 2.x port of sbt-revolver's reStart) |
sbt-uni-crossproject |
crossProject(...) for building one source tree for JVM, Scala.js, and Scala Native — a single-plugin replacement for portable-scala's sbt-crossproject, which is not available for sbt 2.x |
sbt-uni-playwright |
Run Scala.js tests in a real headless browser (Chromium, Firefox, or WebKit) via Playwright — ES module support and a faithful DOM, with no Node.js installation required |
Add the ones you need to project/plugins.sbt:
// HTTP/RPC client generation + background fork-run (~uniRestart)
addSbtPlugin("org.wvlet.uni" % "sbt-uni" % "<version>")
// Cross-build one source tree for JVM, Scala.js, and Scala Native
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.22.0")
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.12")
addSbtPlugin("org.wvlet.uni" % "sbt-uni-crossproject" % "<version>")
// Run Scala.js tests in a real browser
addSbtPlugin("org.wvlet.uni" % "sbt-uni-playwright" % "<version>")For example, sbt-uni-crossproject defines cross-platform projects with
shared sources in src/ and platform-specific code in .jvm/, .js/,
and .native/ folders:
lazy val core = crossProject(JVMPlatform, JSPlatform, NativePlatform)
.crossType(CrossType.Pure)
.in(file("core"))
.settings(/* shared settings */)See the build plugin docs for details.
Apache License 2.0