# Architecture (/docs/concepts/architecture)



An Appbrew app has two halves. The half you ship to the app stores is a React Native shell: navigation setup, the block registry, integration initialization, and the native modules the app needs. The half that decides what users actually see is a JSON config that lives on Appbrew's backend and is fetched when the app launches.

## The launch sequence [#the-launch-sequence]

On launch, the shell fetches the app's config from Appbrew's backend and caches it locally so the app still opens offline. The config describes everything structural:

* **Screens** — which screens exist and which blocks each one contains, in order
* **Blocks** — for each block, its type, its data, its styling, and its behavior flags
* **Theme** — shared visual styling that blocks inherit
* **Settings** — global defaults that apply across block instances
* **Integrations** — per-integration configuration such as API keys and feature flags

The shell then sets up navigation from the config, initializes any configured integrations, and renders the first screen.

## The rendering pipeline [#the-rendering-pipeline]

Each screen renders its blocks in the order the config lists them. A block entry names a `componentId`; the shell looks that id up in the block registry (a map of `componentId` to React component) and renders the matching component. The component reads its own data and styling through hooks, which in turn pull from the app store, Shopify's APIs, and Appbrew services.

```mermaid
flowchart TD
  A[App config JSON] --> B[Screens]
  B --> C[Blocks, in order]
  C --> D[Hooks from @gauntlet/state]
  D --> E[Shopify APIs]
  D --> F[Appbrew services]
```

When a user adds to cart, searches, or opens checkout, the same hooks write back to the store, analytics events fire to every registered tracker, and the UI updates reactively.

## What you write vs what config decides [#what-you-write-vs-what-config-decides]

The split has one practical consequence to hold onto before you write any code:

**UI structure is config. Code defines block types.**

You never hardcode "the home screen shows a hero banner above a product rail." Instead, you write (or reuse) a banner block type and a product rail block type, and the config places instances of them on the home screen in that order. Moving a block, restyling it, or removing it entirely is a config change; it ships without touching code or the app stores.

Conversely, anything the config cannot express yet is a code change: a new block type, a new integration, a new native capability. That code registers itself with the shell (see [Screens and blocks](/docs/concepts/screens-and-blocks)) and then waits for the config to place it.

<Callout type="info" title="Two release tracks">
  Config changes go live when a theme is published: instantly, to every installed app. Code changes ship through an app-store release. Knowing which track a change rides on tells you how fast it can reach users.
</Callout>

## Where to go next [#where-to-go-next]

* [Screens and blocks](/docs/concepts/screens-and-blocks) — the block contract in detail
* [Config and theme](/docs/concepts/config-and-theme) — how styles and settings resolve
* [State and data](/docs/concepts/state-and-data) — the hooks blocks read data through
