README.md

September 1, 2026 · View on GitHub

ZIO Blocks

Modular building blocks for modern Scala applications—no effect system required.

Development CI Badge Maven Central Sonatype Snapshot ZIO Blocks

What Is ZIO Blocks?

ZIO Blocks is a family of type-safe, modular building blocks for Scala applications. Each block is a standalone library with zero or minimal dependencies, designed to work with any Scala stack—ZIO, Cats Effect, Kyo, Ox, Akka, or plain Scala.

The philosophy is simple: use what you need, nothing more. Each block is independently useful and designed to compose with other blocks or your existing code.

Core Principles

  • Zero Lock-In: No dependency on ZIO, Cats Effect, or any other effect system. Use a block with whatever stack you already have.
  • Modular: Each block is a separate artifact. Depend on exactly what you need.
  • Cross-Platform: Most blocks cross-build for JVM and Scala.js, and for Scala 2.13 and 3.x with source compatibility—adopt Scala 3 on your timeline, not ours. The catalog below records the exceptions per block.
  • High Performance: Implementations that avoid boxing, minimize allocations, and use platform-specific features where they pay off.
  • Type Safety: Scala's type system carries the correctness guarantees, without runtime overhead.

Getting Started

Add a block and use it. Nothing else to wire up—no runtime to install, no effect type to adopt:

libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.51"
import zio.blocks.schema._

case class Person(name: String, age: Int)

object Person {
  implicit val schema: Schema[Person] = Schema.derived
}

val alice = Person("Alice", 30)

// One schema, every format
val jsonStr = alice.toJsonString                      // {"name":"Alice","age":30}
val parsed  = """{"name":"Bob","age":25}""".fromJson[Person]

The four blocks below get a full walkthrough because they have no close equivalent elsewhere in Scala. Every other block is one row away in the catalog, and each row links to its own reference page.

All Blocks

Every block is published under the dev.zio organization. Most cross-build for Scala 2.13 and 3.x on both JVM and Scala.js with full source compatibility — adopt Scala 3 on your timeline, not ours. The handful of modules that are narrower say so in their own row.

Schema & Serialization

JSON support is built into zio-blocks-schema; the modules below add further formats.

BlockArtifactPlatformScalaDescription
Schemazio-blocks-schemaJVM · JS2.13 · 3.xType-safe schemas with automatic codec, optic, and validator derivation
Avro Codeczio-blocks-schema-avroJVM2.13 · 3.xApache Avro binary serialization with automatic schema generation
BSON Codeczio-blocks-schema-bsonJVM2.13 · 3.xMongoDB-compatible BSON serialization with native type support
CSV Codeczio-blocks-schema-csvJVM · JS2.13 · 3.xRFC 4180-compliant CSV serialization
MessagePack Codeczio-blocks-schema-messagepackJVM · JS2.13 · 3.xCompact binary serialization with optimized streaming
Thrift Codeczio-blocks-schema-thriftJVM2.13 · 3.xApache Thrift binary serialization with TBinaryProtocol
TOON Codeczio-blocks-schema-toonJVM · JS2.13 · 3.xToken-oriented notation 30–60% smaller than JSON, tuned for LLM prompts
XML Codeczio-blocks-schema-xmlJVM · JS2.13 · 3.xZero-dependency XML serialization with fluent navigation and patching
YAML Codeczio-blocks-schema-yamlJVM · JS2.13 · 3.xHuman-readable YAML serialization with JSON interop

Core Data Types

BlockArtifactPlatformScalaDescription
Chunkzio-blocks-chunkJVM · JS2.13 · 3.xHigh-performance immutable indexed sequences with zero-boxing builders
Maybezio-blocks-maybeJVM · JS2.13 · 3.xLow-allocation optional values backed by null
Combinatorszio-blocks-combinatorsJVM · JS2.13 · 3.xCompile-time composition and decomposition of tuples, eithers, and unions
TypeIdzio-blocks-typeidJVM · JS2.13 · 3.xCompile-time type identity with rich metadata
Contextzio-blocks-contextJVM · JS2.13 · 3.xType-indexed heterogeneous collections
MediaTypezio-blocks-mediatypeJVM · JS2.13 · 3.xType-safe IANA media types with 2,600+ predefined types

Concurrency & Streaming

BlockArtifactPlatformScalaDescription
Asynczio-blocks-asyncJVM · JS2.13 · 3.xZero-allocation asynchronous effect type with direct-style await
Streamszio-blocks-streamsJVM · JS2.13 · 3.xSynchronous pull-based streaming with typed errors and zero boxing
Ring Bufferzio-blocks-ringbufferJVM · JS2.13 · 3.xLock-free bounded ring buffers (SPSC, SPMC, MPSC, MPMC)
Muxzio-blocks-muxJVM · JS2.13 · 3.xThread-safe multiplexer for HTTP/2, QUIC, and WebSocket-style protocols

Resources & Configuration

BlockArtifactPlatformScalaDescription
Scopezio-blocks-scopeJVM · JS2.13 · 3.xCompile-time safe resource management and dependency injection
Configzio-blocks-configJVM · JS2.13 · 3.xTyped configuration loading, feature flags, and rollout rules
Config YAMLzio-blocks-config-yamlJVM · JS2.13 · 3.xYAML source adapter for ConfigSource
Config JSONzio-blocks-config-jsonJVM · JS2.13 · 3.xJSON source adapter for ConfigSource
Config HOCONzio-blocks-config-hoconJVM · JS2.13 · 3.xHOCON source adapter for ConfigSource

Web & HTTP

BlockArtifactPlatformScalaDescription
HTTP Modelzio-blocks-http-modelJVM · JS2.13 · 3.xPure HTTP data model with URL parsing, headers, cookies, and forms
HTTP Model Schemazio-blocks-http-model-schemaJVM · JS2.13 · 3.xSchema-based typed access to the HTTP model
Endpointzio-blocks-endpointJVM · JS2.13 · 3.xType-safe HTTP endpoint descriptors with composable codecs and typed auth
HTMLzio-blocks-htmlJVM · JS2.13 · 3.xType-safe HTML templating with XSS protection
HTMXzio-blocks-http-htmxJVM · JS3.xTyped HTMX DSL for compile-time-checked HTMX attributes
Datastarzio-blocks-datastarJVM · JS3.xTyped Datastar attribute and signal DSL, plus the SSE events that patch a live page
OpenAPIzio-blocks-openapiJVM · JS2.13 · 3.xType-safe OpenAPI 3.1 specification generation and rendering
JWTzio-blocks-jwtJVM · JS2.13 · 3.xZero-dependency JWT signing and verification with HMAC, RSA, ECDSA and EdDSA support

Persistence

BlockArtifactPlatformScalaDescription
SQLzio-blocks-sqlJVM · JS3.xType-safe JDBC wrapper with schema-derived codecs and a CRUD repository
SQL — ZIOzio-blocks-sql-zioJVM3.xZIO integration with ZIO.attemptBlocking and ZLayer
Projectionzio-blocks-projectionJVM3.xEvent-sourced projections with per-entity SQLite storage

Observability

BlockArtifactPlatformScalaDescription
Telemetryzio-blocks-telemetryJVM · JS2.13 · 3.xZero-dependency OpenTelemetry-aligned tracing, logging, and metrics
OTLP Exportzio-blocks-telemetry-otelJVM2.13 · 3.xOTLP exporters bridging telemetry signals to an OpenTelemetry collector

Tooling & Codegen

BlockArtifactPlatformScalaDescription
Codegenzio-blocks-codegenJVM2.13 · 3.xGeneric Scala code generation IR and emitter
Docszio-blocks-markdownJVM · JS2.13 · 3.xGitHub Flavored Markdown parsing, rendering, and programmatic construction
Smithyzio-blocks-smithyJVM2.13 · 3.xSmithy IDL parser and AST library for API modeling

Schema

The Schema block brings dynamic-language productivity to statically-typed Scala. Define your data types once, and derive codecs, validators, optics, and more automatically.

The Problem

In statically-typed languages, you often maintain separate codec implementations for each data format (JSON, Avro, Protobuf, etc.). Meanwhile, dynamic languages handle data effortlessly:

// JavaScript: one line and done
const data = await res.json();

In Scala, you'd typically need separate codecs for each format—a significant productivity gap.

The Solution

ZIO Blocks Schema derives everything from a single schema definition:

case class Person(name: String, age: Int)

object Person {
  implicit val schema: Schema[Person] = Schema.derived
}

// Derive codecs for any format:
val jsonCodec    = Schema[Person].derive(JsonFormat)        // JSON
val avroCodec    = Schema[Person].derive(AvroFormat)        // Avro
val toonCodec    = Schema[Person].derive(ToonFormat)        // TOON (LLM-optimized)
val msgpackCodec = Schema[Person].derive(MessagePackFormat) // MessagePack
val thriftCodec  = Schema[Person].derive(ThriftFormat)      // Thrift

Key Features

  • Universal Data Formats: JSON built in, plus Avro, BSON, CSV, MessagePack, Thrift, TOON, XML, and YAML as separate modules, with Protobuf planned.
  • High Performance: Register-based design stores primitives directly in byte arrays, enabling zero-allocation serialization.
  • Reflective Optics: Type-safe lenses, prisms, and traversals with embedded structural metadata.
  • Automatic Derivation: Derive type class instances for any type with a schema.

Installation

libraryDependencies += "dev.zio" %% "zio-blocks-schema" % "0.0.51"

See the Schema & Serialization rows above for the optional format modules.

Example

import zio.blocks.schema._

case class Address(street: String, city: String)
case class Person(name: String, age: Int, address: Address)

object Person extends CompanionOptics[Person] {
  implicit val schema: Schema[Person] = Schema.derived

  val name: Lens[Person, String] = $(_.name)
  val age: Lens[Person, Int] = $(_.age)
  val streetName: Lens[Person, String] = $(_.address.street)
}

val person = Person("Alice", 30, Address("123 Main St", "Springfield"))
val updated = Person.age.replace(person, 31)

Learn More


Scope

Compile-time verified resource safety for synchronous Scala code. Scope prevents resource leaks at compile time by tagging values with an unnameable type-level identity—values allocated in a scope can only be used within that scope. Child scope values cannot escape to parent scopes, enforced by both the abstract scope-tagged type and the Unscoped constraint on scoped.

The Problem

Resource management in Scala is error-prone:

// Classic try/finally - verbose and easy to get wrong
val db = openDatabase()
try {
  val tx = db.beginTransaction()
  try {
    doWork(tx)
    tx.commit()
  } finally tx.close()  // What if commit() throws?
} finally db.close()

// Using - better, but doesn't prevent returning resources
Using(openDatabase()) { db =>
  db  // Oops! Returned the resource - use after close!
}

The Solution

Scope makes resource leaks a compile error, not a runtime bug:

import zio.blocks.scope.*

Scope.global.scoped { scope =>
  import scope.*

  val db: $[Database] = allocate(Resource(openDatabase()))

  // Methods are hidden - can't call db.query() directly
  // Must use $ to access:
  val result: String = $(db)(_.query("SELECT 1"))

  // Trying to return `db` would be a compile error!
  result  // Only pure data (String) escapes
}
// db.close() called automatically

Key Features

  • Compile-Time Leak Prevention: Values of type scope.$[A] are opaque and unique to each scope instance. Returning a scoped value from its scope is a type error.
  • Zero Runtime Overhead: $[A] erases to A at runtime—zero allocation overhead.
  • Structured Scopes: Child scopes nest within parents; resources clean up LIFO when scopes exit.
  • Built-in Dependency Injection: Wire up your application with Resource.from[T](wires*) for automatic constructor-based DI.
  • AutoCloseable Integration: Resources implementing AutoCloseable have close() registered automatically.
  • Unscoped Constraint: The scoped method requires Unscoped[A] evidence on the return type, ensuring only pure data (not resources or closures) can escape.
  • Actionable Runtime Errors: If a scope reference escapes and is used after closing, allocate, open(), and $ throw IllegalStateException with a detailed message explaining what went wrong, the common causes, and how to fix it—no silent null returns.

Installation

libraryDependencies += "dev.zio" %% "zio-blocks-scope" % "0.0.51"

Example

import zio.blocks.scope.*

final class Database extends AutoCloseable:
  def query(sql: String): String = s"Result: $sql"
  def close(): Unit = println("Database closed")

Scope.global.scoped { scope =>
  import scope.*

  // Allocate returns $[Database] (scoped value)
  val db: $[Database] = allocate(Resource(new Database))

  // Access via $ - result (String) escapes, db does not
  val result: String = $(db)(_.query("SELECT * FROM users"))

  println(result)
}
// Output: Result: SELECT * FROM users
//         Database closed

Learn More


Async

A lightweight, zero-dependency asynchronous effect type. A ready Async[A] is an A, so synchronous code composed with map / flatMap allocates nothing on the happy path while still suspending on genuinely asynchronous work.

The Problem

Asynchronous Scala forces a choice between two costs. Future allocates for every combinator and needs an ExecutionContext threaded everywhere, even when the value is already available. Full effect systems avoid that but ask you to adopt a runtime, a set of type classes, and a programming model across your whole codebase—a heavy price for a library that only occasionally suspends.

The Solution

Async[A] is a value, not a wrapper. When the result is already known, the representation is the result, so composing ready values costs nothing:

import zio.blocks.async._

// Constructors collapse to bare values; transformers inline with no allocation
val computed: Int =
  Async.succeed(20).map(_ + 1).flatMap(n => Async.succeed(n * 2)).block
// computed: Int = 42

Key Features

  • Zero Allocation on the Happy Path: A completed Async[A] is represented as the A itself; map and flatMap over ready values allocate nothing.
  • Direct-Style await: Async.async { ... } rewrites .await calls at compile time into a non-blocking flatMap chain—straight-line code, asynchronous execution.
  • No Runtime to Adopt: No ExecutionContext to thread, no type class hierarchy, no effect system dependency.
  • Interop Built In: Bridges to Future and CompletionStage, plus Async.promise for callback-based APIs.

Installation

libraryDependencies += "dev.zio" %% "zio-blocks-async" % "0.0.51"

Example

Write straight-line asynchronous code with Async.async and .await, rewritten at compile time into a non-blocking flatMap chain:

import zio.blocks.async._

def fetch(id: Int): Async[String] = Async.succeed(s"item-$id")

val program: Async[Int] =
  Async.async {
    val a = fetch(1).await
    val b = fetch(2).await
    (a + b).length
  }

Learn More

  • Getting Started with Async — create, compose, and run async effects
  • Async reference — the full API, including zip, catchAll, collectAll, the Async.promise callback bridge, and Future / CompletionStage interop
  • async-examples — a single-file order-fulfillment demo (sbt "++3.8.3; async-examples/run")

SQL

A thin, type-safe JDBC wrapper that maps Scala case classes to database tables using the same Schema you use for JSON and Avro codecs. No ORM runtime, no code generation — just composable SQL fragments, a derived repository abstraction, and a direct ZIO integration.

The Problem

JDBC is powerful but tedious: manual ResultSet traversal, index-based parameter binding, and repetitive CRUD boilerplate make even simple database access error-prone. ORMs solve the boilerplate but add heavy runtimes, hidden queries, and opaque magic.

The Solution

ZIO Blocks SQL derives everything from a single Schema[A]:

case class User(id: Long, name: String, email: String)
object User:
  given Schema[User] = Schema.derived

// Derive the table, codec, and repository in one line
val repo = Repo.derived[User, Long]

// Use the sql"..." interpolator for custom queries
val frag = sql"SELECT * FROM user WHERE email = ${"alice@example.com"}"

Key Features

  • Schema-derived codecs: DbCodec[A] is auto-derived from Schema[A] — column names, types, and nullability come for free.
  • Composable fragments: The sql"..." interpolator creates Frag values that compose safely with ++. SQL injection is structurally impossible.
  • CRUD repository: Repo[E, ID] provides all, find, findAll, insert, insertAll, update, delete, deleteAll, and clear out of the box.
  • DDL generation: Table.createTable(dialect) generates type-accurate CREATE TABLE IF NOT EXISTS SQL from the schema.
  • ZIO integration: TransactorZIO lifts blocking JDBC calls into Task (or ZIO) with proper bracketing and rollback.
  • Effect-system agnostic core: The zio-blocks-sql module has no ZIO dependency — use it with any effect system or plain Scala.

Installation

libraryDependencies += "dev.zio" %% "zio-blocks-sql" % "0.0.51"

// Optional ZIO integration
libraryDependencies += "dev.zio" %% "zio-blocks-sql-zio" % "0.0.51"

Example

import zio.blocks.schema._
import zio.blocks.sql._
import zio.blocks.sql.zio._

case class Product(id: Long, name: String, price: Double)
object Product:
  given Schema[Product] = Schema.derived
  given DbCodec[Product] = summon[Schema[Product]].deriving(DbCodecDeriver).derive

val repo        = Repo.derived[Product, Long]
val transactor  = TransactorZIO.fromUrl("jdbc:postgresql://localhost/shop", SqlDialect.PostgreSQL)

// Batch insert, then query with a custom filter
val program = transactor.transact:
  repo.insertAll(List(
    Product(1L, "Widget", 9.99),
    Product(2L, "Gadget", 29.99)
  ))
  sql"SELECT * FROM product WHERE price < ${15.0}".query[Product]

Learn More

  • SQL referenceDbCodec, Frag, Table, Repo, Transactor, dialects, and DDL generation
  • Query DSL guide — a four-part series building a type-safe query language on reified optics

Compatibility

ZIO Blocks works with any Scala stack:

StackCompatible
ZIO 2.x
Cats Effect 3.x
Kyo
Ox
Akka
Plain Scala

Each block has zero dependencies on effect systems. Use the blocks directly, or integrate them with your effect system of choice.

Guides

Full API Reference

Every block in the catalog above links to its own reference page. The blocks large enough to have several pages start from an overview:

  • Schema - core type system, dynamic values, optics, validation, and schema evolution
  • Telemetry - tracing, logging, metrics, and OTLP export
  • SQL - codecs, fragments, tables, repositories, transactors, and dialects
  • Resource Management & DI - Scope, Resource, Wire, Unscoped, and finalization
  • Streams - Stream, Pipeline, Sink, and the low-level readers and writers
  • Endpoint - endpoint descriptors, HTTP codecs, route patterns, and typed auth
  • HTTP Model - the pure HTTP data model and its schema-based typed access
  • HTMX - the typed HTMX attribute DSL
  • Ring Buffer - the SPSC, SPMC, MPSC, and MPMC variants
  • Code Generation - the Scala code generation IR and emitter

Documentation

Learn more on the ZIO Blocks homepage!

Contributing

For the general guidelines, see ZIO contributor's guide.

Code of Conduct

See the Code of Conduct

Support

Come chat with us on Badge-Discord.

License

License