C
ConvexCompose
Modules
← Docs

The module contract

Conformance with this contract is what makes something a ConvexCompose module. The contract is opinionated about what a module is and silent about how you consume it. The seminal asymmetry: be opinionated about the module, unopinionated about the host. Every MUST below tightens the module's shape; every MUST NOT protects the host's freedom.

Agnostic core, optional reference UI

Two things are held apart inside every module and must never be collapsed.

The core maximizes reach; the optional reference UI maximizes convenience for the subset who want the paved road. Neither contaminates the other.

The three-layer model

Every module is structured in three layers, ordered most-portable to most-consumer-specific. You only write the top one.

beside Layer 2/3, OPTIONAL: a reference UI package (RN + NativeWind), drop in or ignore
L3
Layer 3: Thin route wrappers (you write this)
~5 lines each · binds Layer 2 into your router + chrome · TanStack / Next / Expo
consumer
L2
Layer 2: Headless React hooks
useX() over Convex hooks · no router · no styling · navigation as callbacks · any renderer
MUST
L1
Layer 1: Convex Component
convex.config.ts + schema + functions · app.use()-installable · isolated · transactional
MUST
C
Convex infra Components
Rate Limiter · Aggregate · Agent · Workpool · the first-wave primitives this builds on
get-convex

MUST

  1. Provide a Convex Component (Layer 1). Ship convex.config.ts, schema, and functions as an isolated, app.use()-installable Component. This is the Convex-native unit of value, with isolation and transactional safety by construction.
  2. Provide headless React hooks (Layer 2). Expose useX() hooks over Convex hooks. They MUST NOT import a router; navigation MUST be expressed via callbacks the consumer supplies; they MUST NOT assume any styling system.
  3. Provide a manifest (portal.config.ts). Declare the module and its entity-link points so a host registry can discover and wire it.
  4. Link entities across modules by explicit ID passed in. A cross-module relationship MUST be expressed by passing a canonical ID (for example the Better Auth userId) into a Component as an explicit argument, never by a cross-Component table join (Convex Components forbid those by design).

Carve-out that keeps M4 idiomatic: deriving identity from ctx for authorization (via requireAuth / requireRole) inside a handler is allowed and expected. M4 governs cross-module linking only. A conforming handler both authenticates its caller from ctx and accepts the canonical foreign-key ID as an explicit argument. The rule of thumb: authorization comes from ctx; cross-module links come from a passed-in ID.

MUST NOT

OPTIONAL (clearly labeled)

Contract-to-layer map

Contract elementLayerStatus
Convex Component (convex.config.ts + schema + functions)Layer 1MUST
Headless useX() hooks, router-free, style-free, callback navigationLayer 2MUST
portal.config.ts manifest with entity-link pointscross-cuttingMUST
Cross-module linking by explicit passed-in ID (no cross-Component joins)Layer 1 boundaryMUST
Identity from ctx for authorization (requireAuth / requireRole)Layer 1 handlerALLOWED
Router import / styling assumption / platform assumptionanyMUST NOT
Reference UI package (RN + NativeWind kit)Layer 3OPTIONAL

Why this is the appeal

Because the module forces no stack on anyone, two very different developers are fully served by the same module. A web-only Next plus shadcn developer installs the Component and the headless hooks and writes their own UI in roughly five-line route wrappers. A cross-platform developer pulls the full reference kit and gets a working surface out of the box. Forcing the full React Native stack on everyone would collapse the addressable audience to the minority building cross-platform, when most are web-only. The agnostic core keeps reach broad; the opinionated reference keeps the road paved. They are the two ends of one contract.

An extension, not a competitor

ConvexCompose builds with and on top of Convex.dev Components (the get-convex project, not the unrelated convex.com CRM). It is the "second wave" Convex's own Stack writing predicted: business-domain components above the infra primitives (rate limiter, aggregate, agent). It is not a rival to Convex's Chef app-builder or to Templates.

Ready to build to the contract? See a module installed and used, then run the checklist.