Layers 2.0 is archived. The latest version is 3.0. Read the 3.0 docs

Read this to understand the problem Layers solves and how it compares to the usual alternatives.

Why Layers

Layers exists because JSON is where structured data goes to get repeated. This page explains the problem, what people usually do about it, and why Layers takes a different path. If you already want the syntax, skip to the JSON guide.

The problem

JSON has no way to say "same as that". Every value is a literal, so the moment two values mean the same thing, one of them is a copy. Real data files accumulate these copies fast:

  • A UI where dozens of widgets repeat the same handful of colors. Changing the palette means finding every copy.
  • A config where dev, staging, and prod are three near-identical files. Every new key must be added three times, and the files drift apart the one week nobody is careful.
  • Game data where every enemy repeats the stats of the enemy it was copied from, and a balance pass touches a hundred files.

The underlying issue is the same in all three: the relationships between values live in the author's head, not in the data. The file records what each value is, but never that this value follows that one, or builds on it, or replaces it under some condition.

What people do about it

Compile-time templating. Tools like Jsonnet, CUE, and Dhall put a language in front of JSON: you write a program with variables, imports, and functions, evaluate it, and emit static output. This genuinely solves authoring duplication, and if you need generated artifacts (Kubernetes manifests, CI config), it is the right shape. But the output is dead data. The relationships exist only during evaluation; the running application receives plain literals with no idea what was derived from what. Change one value and you regenerate and redistribute everything.

Merging in application code. Load a base file, overlay an override file, apply environment logic in code. Every application reinvents this, each with its own ad-hoc merge semantics, and the composition logic ends up scattered through the codebase instead of visible in the data.

The Layers answer

Layers keeps the relationships in the data and resolves them at runtime. Three kinds of composition are authored right in the JSON:

  • Inheritance ("S:Base"): a node builds on another, overriding on top. An instance is just a diff over its archetype.
  • Linking ("L:Path/To/Value"): a value is another value. One source of truth; everything linked follows it.
  • States (":Prod", ":Selected"): one attribute carries several values, and the reader's active states pick one.

Because resolution happens inside your running application, the relationships stay alive after loading:

  • Values follow their sources. Re-value one shared node and every linked value reads the new data immediately. This is how a theme switch re-skins an entire UI with no regeneration step, and the engine can tell you exactly which values derive from which (dependent_attributes, on_change).
  • Conditions are read-time, not build-time. Dev/staging/prod, selected/disabled, narrow/wide: the same loaded tree answers for any combination of active states, so there is nothing to rebuild when the condition changes.
  • Diffs are first-class. A saved game, a user preference set, or a variant product config is a handful of overridden values on top of a base that stays clean.

Layers is also schema-free: it manages structure and resolution and never assigns meaning to your names. The same engine describes a color palette, a widget tree, a service config, a character sheet, or a string table. The examples in the repository's examples/ directory show each of those shapes in about forty lines of JSON apiece.

When not to use Layers

Honesty section. Layers is a C++ engine with a runtime model, and that is not always the right shape:

  • One-shot generation. If you evaluate once and ship static output (manifests, CI config), a compile-time tool like Jsonnet or CUE is simpler and fits existing pipelines.
  • Validation-first configuration. CUE in particular treats types and constraints as the core feature. Layers does not validate your data; it resolves it. If your main problem is enforcing a schema, Layers does not solve it.
  • No C or C++ anywhere. Layers integrates anywhere with a C++ FFI (the integration guide covers JNI and WebAssembly bridges), but a stack with no native layer at all has no place to put the engine.
  • Data with no relationships. A flat settings file with twenty independent keys does not need inheritance, links, or states. Plain JSON is fine.

Where it came from

Layers was born as the styling engine of Vortex, where one JSON style set drives native UIs on desktop, web, and Android. Styling turned out to be the perfect forcing function: theming demanded linking, widget variants demanded inheritance, and interaction and responsiveness demanded states. But none of those features know anything about UI, and the vocabulary was never hardcoded. Layers 2.0 is that engine, offered as what it actually is: JSON inheritance, linking, and states, for whatever your data means.

Appearance
Theme
—