Component Types
AgentWallie supports 19 component types for building paywalls. Every component supports the following top-level properties: type, id, props, style, condition, and animation. Container components (stack, drawer, carousel, slides) also support a top-level children array.
Common Properties
Every component shares this structure:
{
"type": "component_type",
"id": "unique_id",
"props": { ... },
"style": { ... },
"condition": { "field": "...", "operator": "...", "value": "..." },
"animation": { "type": "fade_in", "duration_ms": 300, "delay_ms": 0 }
}| Property | Type | Required | Description |
|---|---|---|---|
type | string | Yes | Component type identifier |
id | string | Yes | Unique identifier for this component |
props | object | Yes | Component-specific properties (see each type below) |
style | object | No | Visual styling (see Common Style Properties below) |
condition | object | No | Conditional visibility rule. See DSL Reference. |
animation | object | No | Entrance animation. See DSL Reference. |
Common Style Properties
All components support these style properties:
| Property | Type | Description |
|---|---|---|
width | string | number | Width ("100%" or fixed points) |
height | string | number | Height |
margin_top | number | Top margin in points |
margin_bottom | number | Bottom margin |
margin_horizontal | number | Left + right margin |
margin_left | number | Left margin |
margin_right | number | Right margin |
padding_top | number | Top padding |
padding_bottom | number | Bottom padding |
padding_horizontal | number | Left + right padding |
padding_vertical | number | Top + bottom padding |
padding_left | number | Left padding |
padding_right | number | Right padding |
background_color | string | Background color |
color | string | Text/foreground color |
text_color | string | Text color (alias) |
corner_radius | string | number | Corner radius |
font_size | number | Font size in points |
alignment | string | Content alignment |
opacity | number | Opacity (0-1) |
border_width | number | Border width |
border_color | string | Border color |
text
Any text block. Supports Liquid templates for dynamic values and markdown bold for inline emphasis.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
content | string | Yes | Text content (supports Liquid: {{ theme.primary }}). Wrap text in **double asterisks** to render it bold. |
text_style | string | No | Predefined style: title1, title2, title3, headline, body, callout, subheadline, footnote, caption |
alignment | string | No | left, center, right |
Markdown Bold
Text content supports markdown-style bold via **text**. The SDK parses these patterns and renders them with a bold font weight using AttributedString. This is useful for highlighting prices or key phrases inline:
{
"props": {
"content": "Free for 3 days. **$29.99/yr after.** Cancel anytime."
}
}Style: letter_spacing
The text component supports a letter_spacing style property for wide tracking, commonly used on uppercase eyebrow labels:
{
"style": { "letter_spacing": 4, "font_size": 11 }
}Example
{
"type": "text",
"id": "title",
"props": {
"content": "Unlock Everything",
"text_style": "title1",
"alignment": "center"
},
"style": {
"color": "{{ theme.text_primary }}",
"margin_bottom": 8
}
}image
Static image from a URL or local resource reference.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
src | string | Yes | Image URL |
alt | string | No | Alt text for accessibility |
aspect_ratio | string | No | Aspect ratio (e.g., "16:9", "1:1") |
fit | string | No | cover, contain, or fill |
Example
{
"type": "image",
"id": "hero",
"props": {
"src": "https://cdn.example.com/hero.png",
"alt": "App preview",
"aspect_ratio": "16:9",
"fit": "cover"
},
"style": { "width": "100%", "margin_bottom": 16 }
}video
Video or animated content.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
src | string | Yes | Video URL |
autoplay | boolean | No | Auto-play on load |
loop | boolean | No | Loop playback |
muted | boolean | No | Mute audio |
aspect_ratio | string | No | Aspect ratio |
Example
{
"type": "video",
"id": "demo",
"props": {
"src": "https://cdn.example.com/demo.mp4",
"autoplay": true,
"loop": true,
"muted": true,
"aspect_ratio": "16:9"
}
}cta_button
Purchase or action button. The primary interactive element.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Button label |
subtitle | string | No | Secondary text (e.g., pricing details) |
action | TapBehavior | Yes | Action to perform (see Tap Behaviors) |
product | string | No | Product slot (for purchase action): "primary", "selected", etc. |
url | string | No | URL (for open_url action) |
action_name | string | No | Custom action name (for custom_action) |
placement_name | string | No | Placement name (for custom_placement) |
Style: glow_color
Set glow_color in the button's style to add a neon glow effect beneath the button. The SDK renders this as a colored drop shadow (.shadow(color: glow_color, radius: 15, y: 4) on iOS).
{
"style": {
"background_color": "#ff0080",
"glow_color": "#61FF0080"
}
}Example
{
"type": "cta_button",
"id": "subscribe",
"props": {
"text": "Start Free Trial",
"subtitle": "{{ products.selected.trial.period }} free, then {{ products.selected.price }}/{{ products.selected.period }}",
"action": "purchase",
"product": "selected"
},
"style": {
"background_color": "{{ theme.primary }}",
"glow_color": "#61FF0080",
"text_color": "#FFFFFF",
"corner_radius": 12,
"height": 56,
"margin_horizontal": 16
}
}product_picker
Product selection UI. Displays available product slots for the user to choose between.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
layout | string | Yes | horizontal, vertical, cards, or toggle |
show_savings_badge | boolean | No | Show savings percentage badge |
savings_text | string | No | Custom badge text (e.g., "BEST VALUE"). Supports Liquid templates. |
show_price | boolean | No | Show the product price on each option. Default: true. |
selected_border_color | string | No | Border color for selected product |
Layout: "cards"
The "cards" layout renders each product as a tall card displaying:
- Plan label (e.g., "WEEKLY", "ANNUAL") from the product slot label
- Price in large text (e.g., "$4.99") pulled from StoreKit / Google Play product data
- Period subtext (e.g., "/week", "/year")
- Selected card: highlighted border via
selected_border_colorwith an optional glow shadow - Savings badge: when
show_savings_badgeis true, a pill badge overlaps the top edge of the best-value card - Savings text: when
savings_textis set, displays custom text (e.g., "Save 88%") below the price in the accent color
This layout is recommended for paywalls where pricing visibility is critical for conversion.
Example (cards layout)
{
"type": "product_picker",
"id": "products",
"props": {
"layout": "cards",
"show_price": true,
"show_savings_badge": true,
"savings_text": "BEST VALUE",
"selected_border_color": "#ff0080"
},
"style": { "margin_bottom": 16, "padding_horizontal": 16 }
}Example (horizontal layout)
{
"type": "product_picker",
"id": "products",
"props": {
"layout": "horizontal",
"show_savings_badge": true,
"savings_text": "Save {{ products.primary.savings_percentage }}%",
"selected_border_color": "{{ theme.primary }}"
},
"style": { "margin_bottom": 16, "padding_horizontal": 16 }
}feature_list
Icon + text list for displaying product features or benefits.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
items | array | Yes | Array of { icon, text } objects |
items[].icon | string | Yes | SF Symbol name (iOS) or Material icon |
items[].text | string | Yes | Feature text |
icon_color | string | No | Icon tint color |
Per-Row Card Styling
Style properties on feature_list are applied to each individual row, not just the outer container. This lets you create a card effect per feature row:
background_color+corner_radius+padding_*create a card look on each rowborder_colorrenders as a leading-edge accent bar (a 3px vertical line on the left side of each row) -- a common paywall design pattern for visually anchoring benefit rows
Example (card-styled rows)
{
"type": "feature_list",
"id": "features",
"props": {
"items": [
{ "icon": "checkmark.circle.fill", "text": "Unlimited workouts" },
{ "icon": "checkmark.circle.fill", "text": "Advanced analytics" },
{ "icon": "checkmark.circle.fill", "text": "Personal coaching" }
],
"icon_color": "{{ theme.accent }}"
},
"style": {
"background_color": "#0d0d1a",
"corner_radius": 5,
"padding_vertical": 8,
"padding_horizontal": 12,
"border_color": "#1FFF0080"
}
}Example (basic)
{
"type": "feature_list",
"id": "features",
"props": {
"items": [
{ "icon": "checkmark.circle.fill", "text": "Unlimited workouts" },
{ "icon": "checkmark.circle.fill", "text": "Advanced analytics" },
{ "icon": "checkmark.circle.fill", "text": "Personal coaching" }
],
"icon_color": "{{ theme.accent }}"
},
"style": { "margin_bottom": 24, "padding_horizontal": 24 }
}link_row
Row of text links, typically used for legal links and restore purchases.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
links | array | Yes | Array of link objects |
links[].text | string | Yes | Link text |
links[].action | TapBehavior | Yes | Action on tap |
links[].url | string | No | URL (for open_url action) |
separator | string | No | Separator between links (e.g., " · ") |
Example
{
"type": "link_row",
"id": "legal",
"props": {
"links": [
{ "text": "Restore Purchases", "action": "restore" },
{ "text": "Terms", "action": "open_url", "url": "https://example.com/terms" },
{ "text": "Privacy", "action": "open_url", "url": "https://example.com/privacy" }
],
"separator": " · "
},
"style": { "font_size": 12, "alignment": "center" }
}stack
Container component for horizontal, vertical, or z-axis (overlay) layouts. Can nest any other components.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
direction | string | Yes | horizontal, vertical, or z |
spacing | number | No | Spacing between children (points, ignored for z) |
alignment | string | No | Cross-axis alignment: leading, center, trailing, top, bottom |
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
children | array | No | Array of child components |
When direction is "z", children are layered on top of each other (like SwiftUI ZStack). The first child is the bottommost layer. Use this for text overlays on images, badges on cards, etc.
Example
{
"type": "stack",
"id": "header",
"props": {
"direction": "horizontal",
"spacing": 12,
"alignment": "center"
},
"children": [
{ "type": "image", "id": "icon", "props": { "src": "https://...", "fit": "contain" }, "style": { "width": 48, "height": 48 } },
{ "type": "text", "id": "app_name", "props": { "content": "My App", "text_style": "headline" } }
]
}drawer
Bottom sheet or expandable section. Contains child components that can be toggled.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
title | string | No | Drawer header text |
expanded | boolean | No | Start expanded. Default: false |
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
children | array | No | Array of child components |
Example
{
"type": "drawer",
"id": "details",
"props": {
"title": "Plan Details",
"expanded": false
},
"children": [
{ "type": "text", "id": "detail_text", "props": { "content": "Your subscription includes..." } }
]
}carousel
Swipeable content carousel with optional auto-scroll.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
auto_scroll | boolean | No | Enable auto-scrolling |
auto_scroll_interval_ms | number | No | Auto-scroll interval in milliseconds |
show_indicators | boolean | No | Show page indicators |
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
children | array | No | Array of child components (one per page) |
Example
{
"type": "carousel",
"id": "testimonials",
"props": {
"auto_scroll": true,
"auto_scroll_interval_ms": 3000,
"show_indicators": true
},
"children": [
{ "type": "text", "id": "quote1", "props": { "content": "Best app ever! - Sarah", "alignment": "center" } },
{ "type": "text", "id": "quote2", "props": { "content": "Changed my life! - Mike", "alignment": "center" } }
]
}slides
Multi-page content with page dots. Similar to carousel but typically used for onboarding-style flows.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
show_page_dots | boolean | No | Show page indicator dots |
Top-Level Fields
| Field | Type | Required | Description |
|---|---|---|---|
children | array | No | Array of child components (one per page) |
Example
{
"type": "slides",
"id": "onboarding",
"props": {
"show_page_dots": true
},
"children": [
{ "type": "text", "id": "slide1", "props": { "content": "Track your workouts", "text_style": "title1", "alignment": "center" } },
{ "type": "text", "id": "slide2", "props": { "content": "Get personalized plans", "text_style": "title1", "alignment": "center" } },
{ "type": "text", "id": "slide3", "props": { "content": "Reach your goals", "text_style": "title1", "alignment": "center" } }
]
}Unlike carousel, slides does not auto-scroll. Users swipe manually between pages, making it ideal for onboarding flows where each page requires user attention.
spacer
Flexible spacing between components.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
height | number | Yes | Fixed height in points |
Example
{
"type": "spacer",
"id": "gap1",
"props": { "height": 24 }
}divider
Horizontal line separator.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
color | string | No | Line color |
thickness | number | No | Line thickness in points |
Example
{
"type": "divider",
"id": "sep1",
"props": { "color": "#E5E7EB", "thickness": 1 }
}countdown_timer
Urgency timer that counts down to zero or a specific time.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
duration_seconds | number | Yes | Countdown duration |
format | string | No | Display format (e.g., "mm:ss", "hh:mm:ss") |
expires_at | string | No | ISO 8601 timestamp to count down to (overrides duration) |
Example
{
"type": "countdown_timer",
"id": "timer",
"props": {
"duration_seconds": 900,
"format": "mm:ss"
},
"style": { "font_size": 32, "alignment": "center", "color": "{{ theme.primary }}" }
}toggle
Switch control for product selection, consent checkboxes, or other boolean inputs.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
label | string | Yes | Toggle label text |
default_value | boolean | No | Initial state. Default: false |
linked_product | string | No | Product slot to select when toggled on |
Example
{
"type": "toggle",
"id": "annual_toggle",
"props": {
"label": "Pay annually (save 40%)",
"default_value": true,
"linked_product": "primary"
}
}survey
Inline survey questions for gathering user feedback.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
question | string | Yes | Survey question text |
options | string[] | Yes | Array of answer options |
allow_multiple | boolean | No | Allow multiple selections. Default: false |
Example
{
"type": "survey",
"id": "exit_survey",
"props": {
"question": "Why are you not subscribing?",
"options": ["Too expensive", "Missing features", "Just browsing", "Other"],
"allow_multiple": false
}
}custom_view
Escape hatch for rendering a native SwiftUI or Jetpack Compose view by name. Register the view in your app code.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
view_name | string | Yes | Registered native view name |
parameters | object | No | Key-value parameters passed to the view |
custom_data | object | No | Arbitrary data passed to the view via CustomViewContext |
Example
{
"type": "custom_view",
"id": "video_preview",
"props": {
"view_name": "WorkoutPreviewView",
"parameters": { "workoutId": "w_123" },
"custom_data": {
"accent_color": "{{ theme.accent }}",
"show_controls": true
}
}
}Custom views must be registered in your app before they can be referenced. See the Custom Views guide for registration API and best practices.
badge
Pill-shaped label for highlighting offers, tiers, or status. Renders as a compact colored tag.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
text | string | Yes | Badge text (e.g., "BEST VALUE", "SAVE 40%") |
variant | string | No | filled (default), outlined, or soft |
Example
{
"type": "badge",
"id": "offer_badge",
"props": {
"text": "BEST VALUE",
"variant": "filled"
},
"style": {
"background_color": "{{ theme.accent }}"
}
}Variants:
filled-- Solid background with white textoutlined-- Transparent background with colored border and textsoft-- Lightly tinted background with colored text
rating
Star rating display with optional count and label. Useful for showing app store ratings or testimonials.
Props
| Prop | Type | Required | Description |
|---|---|---|---|
value | number | Yes | Rating value (e.g., 4.8) |
count | number | No | Number of ratings (e.g., 12500) |
label | string | No | Label text (e.g., "App Store Rating") |
max_stars | number | No | Maximum star count (default: 5) |
Example
{
"type": "rating",
"id": "app_rating",
"props": {
"value": 4.8,
"count": 12500,
"label": "App Store Rating"
},
"style": {
"margin_horizontal": 24,
"margin_bottom": 16
}
}