Skip to content

SYSTEM Cited by 1 source

Zalando Appcraft

What it is

Appcraft is Zalando's server-driven mobile UI framework — the runtime the Zalando iOS + Android apps use to render dynamic content pages whose structure, content, and behaviour are defined per-request by the server rather than baked into the app binary. Design started in 2018 as the replacement for the 2016-era Truly Native Apps (TNA) framework; by 2024 it serves 13 dynamic pages in the Zalando app including the newly-shipped Zalando Stories feature (Source: sources/2024-05-15-zalando-transitioning-to-appcraft-evolution-of-zalandos-server-driven-ui-framework).

Appcraft is SDUI of the Airbnb-Ghost-Platform / Yelp CHAOS lineage: the server emits a JSON screen-configuration tree, the client renders it via a bundled set of primitives. What is distinctive about Appcraft:

Architecture at a glance

  ┌─────────────────────────────────────────────────────┐
  │ Zalando iOS / Android app                           │
  │                                                     │
  │  ┌──────────────────────────────────────────────┐   │
  │  │ Appcraft client                              │   │
  │  │  - Primitives (Label, Button, Image,         │   │
  │  │    Video, Layout) bundled in binary          │   │
  │  │  - Elm runtime (Model/View/Update)           │   │
  │  │  - Flex → native-tree transform              │   │
  │  │    (iOS: Texture  /  Android: Litho)         │   │
  │  │  - Action dispatcher (tap, track, navigate)  │   │
  │  │  - Deep-link resolver (via middleware)       │   │
  │  └──────────────────────────────────────────────┘   │
  └──────────────┬──────────────────────────────────────┘
                 │  fetch screen JSON
  ┌─────────────────────────────────────────────────────┐
  │ Appcraft backend (micro-frontends part-2 system)    │
  │  - Screen-config authoring                          │
  │  - Per-platform / per-app-version / A/B gating      │
  │  - Component-version backward-compat logic          │
  │  - Route configuration for new screens              │
  └─────────────────────────────────────────────────────┘

The deep-link-to-screen resolution runs:

  deep-link → middleware → API endpoint → screen JSON
  (client requests, client doesn't know the API shape)

See concepts/deep-link-to-screen-config-resolution and patterns/deep-link-indirection-middleware.

Component schema

A component in Appcraft is a typed tree node:

{
  "type": "layout",
  "id": "root-container-layout-id",
  "flex": {},
  "props": {},
  "children": [
    {
      "type": "image",
      "id": "id1",
      "flex": {},
      "props": {},
      "events": {
        "tap": [
          {"id": "id2", "props": {}, "type": "track"},
          {"id": "id3", "props": {}, "type": "navigate"}
        ]
      }
    }
  ],
  "events": {}
}

(Source: sources/2024-05-15-zalando-transitioning-to-appcraft-evolution-of-zalandos-server-driven-ui-framework, verbatim from the post.)

Every node carries flex (layout) + props (primitive- specific) + events (ordered action lists). The event vocabulary includes explicit interactions (tap, long-press) and implicit lifecycle events (scroll-forward, dismiss). Actions are the extension point: each action type (track, navigate, …) is implemented on the client.

Primitives (the bundled-in-binary vocabulary)

Five named in the post: Label, Button, Image, Video, Layout container (the universal composition node). Canonicalised as concepts/ui-primitives-as-platform-building-blocks.

Extension to the set requires a client release — e.g. the post mentions Composite Label (sub-text styling / variable font sizes / decoration) was added later because a plain Label couldn't render prices correctly. See concepts/client-release-needed-only-for-new-primitives.

What's on the server, what's on the client

Server (deployed same-day):

  • Screen definitions (component trees).
  • Route configuration (deep-link → API mapping).
  • Component-version policy — per-app-version / per-platform / A/B / premise gating.
  • Backward-compat logic (minimum-app-version per component).
  • Tracking schema (event names, parameters).

Client (requires App Store release):

  • Primitive implementations.
  • Action handlers (navigate, track, …).
  • Flex → Texture/Litho layout transform.
  • Elm runtime.
  • Deep-link resolver client half (but not the route table).

The explicit rule, verbatim from the post: "A client-release is only required when there's a need to introduce a new primitive or extend the contract of an existing one to support additional behaviour."

Testing surfaces

Two, deliberately:

  1. Appcraft Browser — a standalone demo app with a URL address bar. Any endpoint emitting Appcraft screen JSON (including localhost) can be pasted in; the screen renders in an isolated environment with minimal dependencies. Used by web developers (who author screens) to iterate on the renderer logic without building the full Zalando app. Canonicalised as patterns/demo-harness-for-sdui-rendering.
  2. PR-deployed renderer pinning — renderer changes are deployed to staging from a PR; the PR number is entered into the debug build of the real Zalando app, which then exercises the PR's renderer against the full app environment. Canonicalised as patterns/pr-deployed-renderer-testing-in-debug-app.

Business impact (2024, per post)

  • 13 dynamic pages including Zalando Stories.
  • Two tracking-SDK migrations (2021, 2024) with no per-Appcraft-screen mobile work.
  • Multiple app redesigns — notably reduced mobile engineering effort on Appcraft-served pages.
  • Fast-prototyping substrate — engineers + designers iterate on new UI designs within a week.
  • Reduced MTTR — server-side component disable removes need for hotfix releases.
  • Cyber Week battle-tested — performs under Zalando's peak sales load.

Backend companion

The backend serving Appcraft screen JSON is the subject of Zalando's [2021-09 micro-frontends part-2 post] (https://engineering.zalando.com/posts/2021/09/micro-frontends-part2.html), which the Appcraft post explicitly references: "Appcraft has been serving as a stalwart in the realm of backend driven screen frameworks. Read all about the backend system that empowers this platform." That post covers the Rendering Engine architecture — Node.js + custom React reconciler — where the native-apps reconciler path emits JSON instead of HTML. Appcraft is the client-side consumer of that JSON on mobile; the Rendering Engine is the server-side producer.

Contrast with other Zalando UI platforms

  • vs Interface Framework (IF) — IF is the web-native second-gen frontend platform (React + TypeScript + entity tree); IF + Appcraft together form Zalando's two-surface SDUI-ish frontend story (web via React renderers, mobile via primitives + Flex + Elm).
  • vs the custom-React-reconciler for Native Apps path — the 2021-09 micro-frontends part-2 post documents a "Custom React reconciler that emits app-compatible JSON" (see patterns/same-react-code-web-and-native-via-reconciler). Appcraft is the counterpart on the client: it consumes such JSON. The reconciler is one valid JSON-emission path on the server; Appcraft is agnostic to whether the server-side authoring is React-based or not.
  • vs React Native path — Zalando's 2025-10 mobile-RN post describes a newer, parallel cross-platform strategy using React Native for screens that need richer interactivity; Appcraft continues to serve its 13 dynamic-content pages. The two coexist as of 2025.

Comparison with other SDUI systems on-wiki

Property Appcraft Yelp CHAOS
Surface Mobile-native only Web + iOS + Android + Python
UI substrate Texture / Litho Native RN + web DOM
Layout language Flex on native UI framework Platform-specific
Cross-platform codegen None (shared Flex spec) Konbini
Schema version control Server-only Client bundles spec version
Client-release trigger New primitive / new action New component / breaking change
Backend-to-client wire JSON screen tree GraphQL + JSON-string params

Open challenges (named in post)

  1. Monitoring gaps — dynamic screens sometimes see issues reach the platform team only after user-visible impact.
  2. Generality-vs-restrictiveness trade-off when adding new primitive features.
  3. Interoperability — non-Appcraft components inside Appcraft screens (and vice-versa); worked example: Home Screen's tabular structure where each tab is an Appcraft screen embedded in a non-Appcraft host.
  4. Third-party UI library drift — Texture and Litho behaviour varies across OS versions.
  5. Ownership fuzziness — web developers authoring app screens blurs the mobile-vs-web team boundary.

Seen in

Last updated · 550 distilled / 1,221 read