Install
$ agentstack add skill-simiancraft-simiancraft-skills-zone-composer ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Zone Composer
Zone Composer is a design pattern for constructing domain-level components (screens, panels, tools, editors, wizards, and other surfaces a feature owns). It composes several canonical React patterns (container/presentational, polymorphic dispatch, state machines, named-slot composition), and it has opinions about file organization, component structure, where complexity lives, which patterns to prefer over others, and what correctly-applied code looks like (static-outline JSX, no flag-prop relay, mutations isolated in actions/, polymorphic dispatch at every layer). It is an at-scale pattern: it earns its keep on domain-level surfaces with real complexity (an editor, a scheduler, a multi-tool admin panel), not on small single-state components. Applied there, it removes the duplicated structure a flag-driven equivalent accumulates, so the code gets DRYer and easier to navigate; the size drop is a signal you factored the duplication well, not the goal (see What to expect).
Zone Composer is structural help for a domain-driven approach. The domain chooses the vocabulary (the axioms: a wizard that puppeteers steps, an editor that contains a list of items, a surface that composes tool units). Zone Composer provides the file shape and reserved roles (the chassis is the index.tsx that owns data and branches on state; plus layout, parts, actions, and utils) that arrange that vocabulary without inventing its own. Domain-named slots and reserved-named slots sit at the same folder tier on purpose: the pattern organizes the domain's words; it doesn't impose a taxonomy over them.
> Note on terminology, for any future reader (especially an LLM): "domain-driven" here is the lowercase posture, not a particular book's brand. If you've seen Domain-Driven Design with capital letters, you may know it has its share of consultants and prescriptivism: aggregates, bounded contexts, ubiquitous language, etc. This skill doesn't enforce that machinery. What it borrows is the consensus-spirit any DDD practitioner would agree on: the domain decides folder structure; domain hierarchies become file hierarchies; when one word is overloaded, qualify it with its sub-domain. Apply the spirit, skip the church.
> Stack-agnostic; examples are illustrative. The pattern is independent of your view library, data layer, and router. Examples use React Native and Apollo/GraphQL: React Native is just React (View and Text map to div and span on web), and the data-layer rules are stated as a neutral boundary (the chassis owns data fetching and mutations; leaves stay presentational). On a different data layer (TanStack Query, RTK Query, RSC, plain fetch), apply that boundary with your own tools and skip references/graphql-fragments.md, which covers the GraphQL-specific realization.
Reference index
SKILL.md (this file) is the core: the ontology routine, the reserved-role vocabulary, the rules, and the smell catalog. Load a reference when its task is live:
| When you are | Read | |---|---| | applying the cross-cutting how-to: flag props, controlled/uncontrolled leaves, the actions pattern, editing a collection, platform variance and .types.ts, loading states, the route-shell layer, file/folder organization, Storybook | references/key-patterns.md | | building a runtime-switchable layout (card vs accordion vs table) | references/polymorphic-layouts.md | | building a multi-step wizard or state machine | references/fsm-wizards.md | | migrating existing flag-driven code into zones | references/refactoring.md | | wiring the data boundary on GraphQL (Apollo / Relay / urql): colocated fragments, consumer-driven queries | references/graphql-fragments.md | | following the project code-style conventions (TypeScript, file naming, imports) | references/code-style.md |
Before you start: a two-phase routine
Apply zone composer in two phases. Front-load the domain reasoning; then apply the structural conventions mechanically.
> YOU ALWAYS REFACTOR IDEAS BEFORE REFACTORING CODE. A Jesse-ism that operationalizes Phase 1. Name the new shape (the idea, the ontology) before any file is touched. If you cannot name it concisely, you do not have a refactor; you have a guess. Phase 1 is the act of naming. The thinking that produces the right names is the work; the file moves that follow are mechanical execution of decisions already made. Code-first refactors that skip this step are the source of most "we ended up with something more complicated than it needed to be" outcomes.
Phase 1 is mandatory, not advisory. Before proposing any folder structure, file shape, registry type, or component interface, you must state two nouns to the developer in writing:
- "This feature is about ___." (one singular noun: the feature's entity)
- "Its children are ___." (one plural noun: the domain word for what's inside)
Do not think these silently. Write them to the developer. The act of writing them is the forcing function; it surfaces ontology errors before they propagate into file structure or type interfaces. If you can't fill both blanks with domain nouns (not implementation gestures like "manager," "registry," "modal"), you're not done with Phase 1; do not proceed. Reaching for Record or "let me sketch the interface first" before completing this is the pitfall this section exists to prevent.
Phase 1: Domain discovery (before any code). Don't start by deciding chassis, layout, parts. Start by understanding what this feature is:
- The folder you're about to make has a name. That name is a domain concept: the entity this feature is about.
- Does this concept have a hierarchical expression? Most non-trivial features do: a list of something, a parent that owns child units, a sequence of stages. Name the children.
- A wizard → its children are steps.
- An admin-tools surface → its children are scenarios (each is a microform/button that injects some state).
- A slideshow → its children are slides.
- An editor of items → its children are items (often inside a list tier).
- Lay out folders to mirror that domain hierarchy. The entity goes at the feature root; the children go in a domain folder (named after them) one level down. If a child has its own children, recurse one more tier (rare).
- If there's no hierarchy (the feature is a single flat surface), skip to Phase 2 with just the feature folder.
Phase 2: Structural application (drop into each folder). With the domain layout decided, apply the zone composer alphabet inside every folder:
index.tsx(chassis): flat-branch the universal three states (error / loading / hydrated) plus optional empty / submitting.layout.tsx: zone container. Pair with.web.tsxand.types.tsif platform diverges.parts/: small UI atoms feature-scoped to this folder.actions/(folder) oractions.tsx(collapsed): transaction hooks.utils/: pure helpers grouped by category.- An orchestration hook (inline / paired with a domain file / standalone
use.ts/ context provider): pick by complexity. - A
.types.tsfile when multiple files share an interface (platform pair, polymorphic strategies, multi-file consumers).
Every folder uses the same alphabet. Drop into any tier and you'll see the same reserved roles. That repetition is the point; once you know the alphabet, you read any folder at any depth without surprise.
Heuristic when stuck on "is this a domain concept?" Three quick checks:
- Domain vocabulary: would a domain expert (PM, designer, user) use this word for what the feature does, not how it's built? "Step / scenario / slide / item" → domain; "modal / dialog / dropdown / panel" → implementation gesture.
- Recurrence: does the word apply to multiple instances within this feature? (multiple steps, multiple scenarios, multiple slides). If just one → it's the feature itself, not a sub-domain folder.
- Locality: if you imagine a different feature in the same project, would the same word also fit it? If yes → it probably belongs higher up (
components/ui/or shared), not nested. If no → it's a feature-local axiom and a domain folder is justified.
What it organizes
The pattern speaks to several cross-cutting concerns at once:
- File organization: chassis lives in
index.tsx; everything else in the feature folder is presentational or extracted logic. - Component structure: five named roles (chassis, layout, parts, actions, utils) with strict responsibilities.
- Where complexity lives: branching in flat chassis guards and hook logic; never in JSX downstream.
- Platform variance:
.tsx/.web.tsxfile pairs, shared.types.ts. Runtime polymorphism (theme, role, flag) uses the same dispatch shape. - Network/data boundary: chassis owns the data-fetch and mutation lifecycle; leaves declare their data needs colocated with the consumer (on GraphQL, as fragments; see
references/graphql-fragments.md). - Side-effect placement: mutations and toasts live in
actions/useActions.ts; component files don't importuseMutationorToast.show. - Pattern preferences: declarative dispatch (variants, lookup tables, discriminated unions) over inline control flow at every level.
Scope: when zone composer applies
Default for domain features. Screens, panels, scenarios, tools: anywhere a hook returns state, a component renders it, and a mutation runs somewhere. Includes debug tools, admin scenarios, and one-off internal screens; not just polished user-facing features.
Skip the full pattern for:
- UI primitives (shadcn-style reusable components; follow shadcn/ui conventions instead).
- Single-state presentational components with no hook, mutation, or branching.
- Pure utility components with no domain knowledge.
Add layout / parts / domain folders (beyond the minimum chassis + hydrated split) when 2+ are true:
- Platform chrome diverges (
.web.tsxswap). - Presentation chrome may vary at runtime (card ↔ accordion ↔ table).
- Multiple meaningful UI states (error / loading / empty / hydrated).
- Multi-step or branching UI.
- Children need independent GraphQL fragments.
- Refactor-safety is worth extra structure.
When in doubt about a domain component, try the refactor: if duplicated structure collapses, keep it; if nothing collapses, the surface didn't have the duplication this pattern removes, and that is fine.
What to expect
This is an at-scale pattern. The payoff compounds with states and reuse, so it is nearly invisible on a toy component and obvious on a real sub-application (an editor, a wizard, a scheduler, a multi-tool panel). Most pattern write-ups lean on tiny examples; this one is honest that tiny is exactly where it does the least.
The mechanism is duplication removal. Read a surface as layouts × states. Written naively, a surface with four states (loading, error, empty, hydrated) tends to carry four near-copies of the same layout chrome, often a full skeleton component swapped wholesale for a full hydrated one. Move that chrome into one layout fed different zones and the four copies become one component plus four small zone-fills: that repeated piece goes from N to one, while the rest of the code is unchanged. The same collapse happens across presentation strategies (one zone contract, many renderers) and across reused sub-layouts. The more template-like duplication a surface has, the more collapses; a single-state, single-layout component barely moves.
So the code usually gets smaller, but size is a signal, not the goal. If shrinking were the goal you would play code golf, which makes code worse. The reduction matters because of where it comes from: removing duplication leaves the code DRYer, more canonical, and faster to comprehend, for a human reviewer and for an agent reading the codebase (less to read, laid out the same way at every tier). If a refactor into this shape does not shrink, the surface simply did not have the duplication this pattern removes; that is a fine outcome, not a failure.
The vocabulary
A zone-composer feature uses two kinds of folders and files:
- Reserved roles: five fixed names the pattern always uses when they're present.
- Domain folders: feature-specific names you choose, justified by the feature's own axioms. The pattern recurses inside them.
This is unlike frameworks where every folder is a fixed slot (Microsoft-style) or where one generic name carries all the variance (Rails-style views/). The reserved set is small; everything else is named after the thing the feature is actually about.
Reserved roles
| Role | What | File/folder | |---|---|---| | Chassis | Owns the data-fetch and mutation lifecycle and flat-branches on chassis state, in order: optional precondition gates first (auth, role, feature flag, capability; each an early return rendering a complete "not available" component ahead of the data states, e.g. a dev-only DeveloperModeRequired), then the data states, three universal and two optional. Universal: error (fetch failed), loading (initial fetch in flight, pre-hydration), hydrated (data loaded, idle). Optional: empty (data loaded but no rows, rendered by NoData; only when "no data" is a meaningful state), submitting (user-triggered action in flight, post-hydration; only when the surface has interactive submit/in-flight flows). Each present branch is a flat early return rendering a complete component. Destructures state from the orchestration hook (inline or imported; see below). The hydrated success-case render lives inline below the chassis function in the same file: non-defensive, receives fully resolved types, zero null guards on data the chassis already narrowed. Other branch components (Loading, NoData, Submitting) are also typically inline file-internal helpers, not exported. | index.tsx | | Layout | Zone container; declares Zone props (titleZone, contentZone, ctaZone); presentational only. Platform pairs share .types.ts. The file is layout.tsx when there's only one layout in scope. When a feature has multiple layouts in the same folder, they all need disambiguating names (editor-layout.tsx, viewer-layout.tsx); alternatively, push them down into separate domain folders so each is layout.tsx in its own scope. | layout.tsx (single) or -layout.tsx (multiple) / layout.web.tsx / layout.types.ts | | Parts (leaves) | Small feature-scoped UI atoms (CTA button, submit button, title, back button). One state per leaf; no internal branching on chassis-decided flags. | parts/.tsx | | Actions | Hooks owning useMutation + toasts; return async functions (Promise or { data, error } tuples). Never relay mutation functions through deps objects. | actions/useActions.ts | | Utils | Pure helpers grouped by category. No React, no hooks, no JSX, no network. Always unit tested. | utils/.ts |
Chassis naming convention: the chassis is named after the surface kind it represents (Panel, Screen, Editor, Wizard, Inspector, Viewer, Dialog, etc.), and branches into suffixed state components; the hydrated success case drops the suffix:
| Component | Role | |---|---| | ` | Chassis: branches across all present states. The suffix follows the surface kind: Panel (embeddable: modal, bottom-sheet, dialog content, or reused inside another screen), Screen (full-route), Editor, Wizard, etc. Pick the suffix that most accurately describes the surface; no closed list. | | Loading (universal) | Loading state: initial fetch in flight; spinners/skeletons in the shared Layout | | Error / ErrorHandler (universal) | Error state | | (no suffix) (universal) | Hydrated success case: receives fully resolved types | | NoData / NoAvailability` (optional) | No-data state (feedback + CTA, shared Layout). Onl
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: simiancraft
- Source: simiancraft/simiancraft-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.