Embedded Mini Apps
This is a practical, end-to-end technical guide for building miniapps that run inside the Circles miniapp host and communicate through @aboutcircles/miniapp-sdk.
If you are a vibecode developer, you can simply copy the entire page using the button above and paste in your coding environment
Mental Model: How Embedded Miniapps Run?
A embedded miniapp is a web app rendered in an iframe inside a host application.
Your app owns UI, local state, validation, and business flow.
The host owns wallet session UX and transaction approval.
@aboutcircles/miniapp-sdkbridges both sides usingpostMessage.
This means:
Local browser testing is useful for UI and logic.
End-to-end wallet/transaction testing must happen in the host.
SDK Capabilities
The SDK currently exposes:
isMiniappMode(): booleanonAppData(fn: (data: string) => void): voidonWalletChange(fn: (address: string | null) => void): () => voidsendTransactions(transactions: Transaction[]): Promise<string[]>signMessage(message: string, signatureType?: 'erc1271' | 'raw'): Promise<{ signature: string; verified: boolean }>
Where Transaction is:
Project Setup
Create the app
Add additional Circles SDK packages only if your use case needs them:
Suggested structure
Keep host bridge logic isolated from domain logic.
Bootstrapping the Host Bridge
Create a dedicated bridge module:
Use onWalletChange as the app’s wallet truth source.
Wallet Lifecycle Pattern
Implement wallet lifecycle as a finite-state flow:
disconnectedconnecting(optional UI state)connectederror(recoverable or fatal)
Minimal pattern:
Rules:
Always reset account-scoped caches on wallet changes.
Never assume prior in-memory state is still valid after reconnects.
Transaction Submission Pattern
Always format and submit transactions through one adapter.
Why this is critical:
Prevents value-encoding inconsistencies.
Keeps write paths auditable.
Makes retries and logging straightforward.
Standard Action Pipeline
For every write action (any state-changing on-chain operation), use this pipeline:
Validate input
Normalize addresses/amounts
Optional preflight simulation
Build one or more tx payloads
Show pending UI
Submit via host bridge
Wait for confirmations (if required by UX)
Show success/failure
Refresh affected data
Example shell:
Signing Flows (signMessage)
signMessage)Use signMessage when your backend or protocol needs host-backed signatures.
Guidance:
Prefer
'erc1271'unless you explicitly require raw bytes semantics.Persist signature type with the signature payload for verifier correctness.
App Data Injection (onAppData)
onAppData)The host can pass app-specific context via query data.
Use cases:
entry context (resource id, mode, source)
feature flags
campaign or referral metadata
Pattern:
Never trust raw host data without validation.
Validation and Type Safety
Minimum validation set:
address: checksum normalization, strict format checksamount: decimal precision + non-negative + max boundsurl: protocol allowlist (https://by default)text: length limits and rendering-safe escaping
Keep pure validation helpers in a dedicated module and test them directly.
Error Handling and Recovery
Design a predictable error model:
validation_erroruser_rejectednetwork_errorhost_bridge_errorunexpected_error
Normalize unknown errors:
For recoverable host issues:
show actionable guidance
preserve user input where safe
do not silently retry state-changing actions
Concurrency and Race Control
Use request IDs for async operations that may race:
Apply to:
debounced search
paginated loads
wallet-dependent initialization
Generic Starter Skeleton
Last updated
Was this helpful?