Skip to content
Brok UI

Loading…

No results

Checkout

Open source

Checkout pages — a contact/shipping/payment form beside a sticky order-summary card with promo code and totals.

Preview

resources/views/blocks/checkout-subscription.blade.php Blade
@props([
    'planName'    => 'Pro',
    'monthlyPrice' => 29,
])

<section
    aria-label="{{ __('Subscription checkout') }}"
    data-slot="checkout-01"
    {{ $attributes->merge(['class' => 'bg-background py-12 text-foreground sm:py-16']) }}
    x-data="{
        annual: false,
        get monthly() { return {{ (int) $monthlyPrice }}; },
        get price() {
            return this.annual
                ? Math.round(this.monthly * 0.8 * 12)
                : this.monthly;
        },
        get suffix() {
            return this.annual ? '/{{ __('yr') }}' : '/{{ __('mo') }}';
        },
        get cycle() {
            return this.annual ? '{{ __('Annual') }}' : '{{ __('Monthly') }}';
        },
        get disclosure() {
            const amt = this.annual
                ? '$' + Math.round(this.monthly * 0.8 * 12)
                : '$' + this.monthly;
            const period = this.annual ? '{{ __('year') }}' : '{{ __('month') }}';
            return '{{ __("You\'ll be charged") }} ' + amt + ' {{ __('every') }} ' + period + '. {{ __('Cancel anytime.') }}';
        },
        init() {},
        destroy() {},
    }"
>
    <div class="mx-auto max-w-5xl px-4">

        <div class="grid gap-10 lg:grid-cols-[1fr_360px] lg:items-start [&>*]:min-w-0">

            {{-- ─── LEFT: Form ───────────────────────────────────────────── --}}
            <div class="space-y-6">

                {{-- Billing cycle toggle --}}
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Billing cycle') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content>
                        <div class="flex items-center justify-between gap-4">
                            <div class="flex items-center gap-4">
                                <span
                                    class="text-sm font-medium transition-colors"
                                    :class="annual ? 'text-muted-foreground' : 'text-foreground'"
                                >{{ __('Monthly') }}</span>

                                <x-ui.switch
                                    x-model="annual"
                                    aria-label="{{ __('Toggle annual billing') }}"
                                />

                                <span
                                    class="text-sm font-medium transition-colors"
                                    :class="annual ? 'text-foreground' : 'text-muted-foreground'"
                                >{{ __('Annual') }}</span>
                            </div>

                            <x-ui.badge variant="success">{{ __('Save 20%') }}</x-ui.badge>
                        </div>

                        <p class="mt-4 text-sm text-muted-foreground">
                            <span x-text="'{{ __(':plan — billed ') }}'.replace(':plan', '{{ $planName }}') + cycle.toLowerCase()">{{ $planName }} — billed monthly</span>
                        </p>
                    </x-ui.card.content>
                </x-ui.card>

                {{-- Payment details --}}
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Payment details') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content>
                        <form method="POST" action="#" class="space-y-4">
                            @csrf

                            {{-- Card number --}}
                            <div class="space-y-2">
                                <x-ui.label for="card-number">{{ __('Card number') }}</x-ui.label>
                                <x-ui.input
                                    id="card-number"
                                    name="card_number"
                                    type="text"
                                    inputmode="numeric"
                                    autocomplete="cc-number"
                                    placeholder="1234 5678 9012 3456"
                                    class="w-full"
                                />
                            </div>

                            {{-- Name on card --}}
                            <div class="space-y-2">
                                <x-ui.label for="card-name">{{ __('Name on card') }}</x-ui.label>
                                <x-ui.input
                                    id="card-name"
                                    name="card_name"
                                    type="text"
                                    autocomplete="cc-name"
                                    placeholder="{{ __('Jane Smith') }}"
                                    class="w-full"
                                />
                            </div>

                            {{-- Expiry + CVC --}}
                            <div class="grid grid-cols-2 gap-4">
                                <div class="space-y-2">
                                    <x-ui.label for="card-expiry">{{ __('Expiry') }}</x-ui.label>
                                    <x-ui.input
                                        id="card-expiry"
                                        name="card_expiry"
                                        type="text"
                                        inputmode="numeric"
                                        autocomplete="cc-exp"
                                        placeholder="MM / YY"
                                        class="w-full"
                                    />
                                </div>
                                <div class="space-y-2">
                                    <x-ui.label for="card-cvc">{{ __('CVC') }}</x-ui.label>
                                    <x-ui.input
                                        id="card-cvc"
                                        name="card_cvc"
                                        type="text"
                                        inputmode="numeric"
                                        autocomplete="cc-csc"
                                        placeholder="123"
                                        class="w-full"
                                    />
                                </div>
                            </div>

                            {{-- Submit --}}
                            <x-ui.button type="submit" class="w-full">
                                <x-slot:icon>
                                    <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                        <rect width="20" height="14" x="2" y="5" rx="2" />
                                        <path d="M2 10h20" />
                                    </svg>
                                </x-slot:icon>
                                {{ __('Subscribe') }}
                            </x-ui.button>

                        </form>
                    </x-ui.card.content>
                </x-ui.card>

            </div>

            {{-- ─── RIGHT: Order summary ──────────────────────────────────── --}}
            <div class="lg:sticky lg:top-8">
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Order summary') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content class="space-y-4">

                        {{-- Plan row --}}
                        <div class="flex items-center justify-between gap-2">
                            <div>
                                <p class="font-medium text-foreground">{{ $planName }}</p>
                                <p class="text-sm text-muted-foreground" x-text="cycle + ' {{ __('billing') }}'">{{ __('Monthly billing') }}</p>
                            </div>
                            <p class="tabular-nums font-semibold text-foreground">
                                $<span x-text="price">{{ $monthlyPrice }}</span><span x-text="suffix">/mo</span>
                            </p>
                        </div>

                        <x-ui.separator />

                        {{-- Tax --}}
                        <div class="flex items-center justify-between gap-2 text-sm text-muted-foreground">
                            <span>{{ __('Taxes') }}</span>
                            <span>{{ __('Calculated at checkout') }}</span>
                        </div>

                        <x-ui.separator />

                        {{-- Total --}}
                        <div class="flex items-center justify-between gap-2">
                            <span class="font-semibold text-foreground">{{ __('Total') }}</span>
                            <p class="tabular-nums font-bold text-foreground">
                                $<span x-text="price">{{ $monthlyPrice }}</span><span x-text="suffix">/mo</span>
                            </p>
                        </div>

                        {{-- Disclosure --}}
                        <p class="text-xs text-muted-foreground" x-text="disclosure">
                            {{ __("You'll be charged $:price every month. Cancel anytime.", ['price' => $monthlyPrice]) }}
                        </p>

                    </x-ui.card.content>
                    <x-ui.card.footer class="flex items-center gap-2 border-t border-border pt-4 text-xs text-muted-foreground">
                        <svg class="size-4 shrink-0 text-muted-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <rect width="11" height="11" x="3" y="11" rx="2" ry="2" />
                            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
                        </svg>
                        {{ __('Secure checkout') }}
                    </x-ui.card.footer>
                </x-ui.card>
            </div>

        </div>
    </div>
</section>

Installation

terminal
php artisan ui:add blocks/checkout-subscription

Installs these dependencies automatically:

Registry contract

Install confidence

php artisan ui:add blocks/checkout-subscription writes only the generated targets below. The CLI validates each file hash before writing and prompts before replacing local changes unless --force is used.

Version
1.1.1
License
open
Stability
stable
Contract
v1
Foundation
≥ 1.0.0
Type Generated target
blade resources/views/blocks/checkout-subscription.blade.php

Registry dependencies

badge button card input label separator switch

Package dependencies

composer: jml/brok:^0.2

Use with AI

A brief for your coding agent: what the block is, the install command, how to render it, its props and the rules. Copy it, or open a prompt about this block in an assistant.

checkout-subscription.md
# Brok UI block: Checkout Subscription (`checkout-subscription`)

Two-column subscription checkout with a monthly/annual billing toggle, live price recalculation, payment fields, and a sticky order summary card.

Brok UI is a Laravel Blade registry. `ui:add` copies this block into the app as plain Blade the app owns; it composes installed `<brok:*>` primitives and semantic design tokens.

## Install

```bash
php artisan ui:add blocks/checkout-subscription
```

## Render it

```blade
<x-blocks.checkout-subscription />
```

## Props

- `plan-name` (string, default `Pro`)
- `monthly-price` (int, default `29`)

## Use when

- Checking a subscriber out with a monthly/annual billing toggle that recalculates the price live before payment.
- Pairing payment fields with a sticky order-summary card that reflects the chosen billing cycle.

## Avoid when

- Use checkout-two-column for a cart-based e-commerce checkout, or checkout-express for a single-column express checkout instead.

## Rules

- Render the installed block with `<x-blocks.checkout-subscription />` and pass data through its props; edit the copied file only for structural changes.
- Keep the semantic design tokens (`bg-background`, `text-muted-foreground`); never swap in raw colour utilities.
- Keep the `data-slot` attributes and the logical (start/end) spacing so the markup still mirrors under `dir="rtl"`.

## Links

- Docs: https://brokui.dev/blocks/checkout-subscription
- Registry JSON (files, props, contract): https://brokui.dev/r/open/blocks/checkout-subscription.json

Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.

Guidance

Use when

  • Checking a subscriber out with a monthly/annual billing toggle that recalculates the price live before payment.
  • Pairing payment fields with a sticky order-summary card that reflects the chosen billing cycle.

Avoid when

  • Use checkout-two-column for a cart-based e-commerce checkout, or checkout-express for a single-column express checkout instead.

Anti-patterns

  • Do not replace semantic props with conflicting utility classes.
  • Do not remove labels, focus styles, or state attributes.
Anatomy
root checkout-01

Usage

Render the block as a component, passing data where useful:

resources/views/welcome.blade.php Blade
<x-blocks.checkout-subscription />

Built from primitives

Composes <brok:*> Semantic tokens Re-themeable RTL-ready

This block composes installed <brok:*> primitives and semantic design tokens only — it does not reimplement any primitive. Re-theme it (light, dark, admin, customer) by editing CSS variables; flip the preview to RTL to confirm it mirrors.

Source

The exact, editable file ui:add writes into your app. The preview above renders this same source — there are no preview-only blocks.

resources/views/blocks/checkout-subscription.blade.php Blade
@props([
    'planName'    => 'Pro',
    'monthlyPrice' => 29,
])

<section
    aria-label="{{ __('Subscription checkout') }}"
    data-slot="checkout-01"
    {{ $attributes->merge(['class' => 'bg-background py-12 text-foreground sm:py-16']) }}
    x-data="{
        annual: false,
        get monthly() { return {{ (int) $monthlyPrice }}; },
        get price() {
            return this.annual
                ? Math.round(this.monthly * 0.8 * 12)
                : this.monthly;
        },
        get suffix() {
            return this.annual ? '/{{ __('yr') }}' : '/{{ __('mo') }}';
        },
        get cycle() {
            return this.annual ? '{{ __('Annual') }}' : '{{ __('Monthly') }}';
        },
        get disclosure() {
            const amt = this.annual
                ? '$' + Math.round(this.monthly * 0.8 * 12)
                : '$' + this.monthly;
            const period = this.annual ? '{{ __('year') }}' : '{{ __('month') }}';
            return '{{ __("You\'ll be charged") }} ' + amt + ' {{ __('every') }} ' + period + '. {{ __('Cancel anytime.') }}';
        },
        init() {},
        destroy() {},
    }"
>
    <div class="mx-auto max-w-5xl px-4">

        <div class="grid gap-10 lg:grid-cols-[1fr_360px] lg:items-start [&>*]:min-w-0">

            {{-- ─── LEFT: Form ───────────────────────────────────────────── --}}
            <div class="space-y-6">

                {{-- Billing cycle toggle --}}
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Billing cycle') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content>
                        <div class="flex items-center justify-between gap-4">
                            <div class="flex items-center gap-4">
                                <span
                                    class="text-sm font-medium transition-colors"
                                    :class="annual ? 'text-muted-foreground' : 'text-foreground'"
                                >{{ __('Monthly') }}</span>

                                <x-ui.switch
                                    x-model="annual"
                                    aria-label="{{ __('Toggle annual billing') }}"
                                />

                                <span
                                    class="text-sm font-medium transition-colors"
                                    :class="annual ? 'text-foreground' : 'text-muted-foreground'"
                                >{{ __('Annual') }}</span>
                            </div>

                            <x-ui.badge variant="success">{{ __('Save 20%') }}</x-ui.badge>
                        </div>

                        <p class="mt-4 text-sm text-muted-foreground">
                            <span x-text="'{{ __(':plan — billed ') }}'.replace(':plan', '{{ $planName }}') + cycle.toLowerCase()">{{ $planName }} — billed monthly</span>
                        </p>
                    </x-ui.card.content>
                </x-ui.card>

                {{-- Payment details --}}
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Payment details') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content>
                        <form method="POST" action="#" class="space-y-4">
                            @csrf

                            {{-- Card number --}}
                            <div class="space-y-2">
                                <x-ui.label for="card-number">{{ __('Card number') }}</x-ui.label>
                                <x-ui.input
                                    id="card-number"
                                    name="card_number"
                                    type="text"
                                    inputmode="numeric"
                                    autocomplete="cc-number"
                                    placeholder="1234 5678 9012 3456"
                                    class="w-full"
                                />
                            </div>

                            {{-- Name on card --}}
                            <div class="space-y-2">
                                <x-ui.label for="card-name">{{ __('Name on card') }}</x-ui.label>
                                <x-ui.input
                                    id="card-name"
                                    name="card_name"
                                    type="text"
                                    autocomplete="cc-name"
                                    placeholder="{{ __('Jane Smith') }}"
                                    class="w-full"
                                />
                            </div>

                            {{-- Expiry + CVC --}}
                            <div class="grid grid-cols-2 gap-4">
                                <div class="space-y-2">
                                    <x-ui.label for="card-expiry">{{ __('Expiry') }}</x-ui.label>
                                    <x-ui.input
                                        id="card-expiry"
                                        name="card_expiry"
                                        type="text"
                                        inputmode="numeric"
                                        autocomplete="cc-exp"
                                        placeholder="MM / YY"
                                        class="w-full"
                                    />
                                </div>
                                <div class="space-y-2">
                                    <x-ui.label for="card-cvc">{{ __('CVC') }}</x-ui.label>
                                    <x-ui.input
                                        id="card-cvc"
                                        name="card_cvc"
                                        type="text"
                                        inputmode="numeric"
                                        autocomplete="cc-csc"
                                        placeholder="123"
                                        class="w-full"
                                    />
                                </div>
                            </div>

                            {{-- Submit --}}
                            <x-ui.button type="submit" class="w-full">
                                <x-slot:icon>
                                    <svg class="size-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                                        <rect width="20" height="14" x="2" y="5" rx="2" />
                                        <path d="M2 10h20" />
                                    </svg>
                                </x-slot:icon>
                                {{ __('Subscribe') }}
                            </x-ui.button>

                        </form>
                    </x-ui.card.content>
                </x-ui.card>

            </div>

            {{-- ─── RIGHT: Order summary ──────────────────────────────────── --}}
            <div class="lg:sticky lg:top-8">
                <x-ui.card>
                    <x-ui.card.header>
                        <x-ui.card.title>{{ __('Order summary') }}</x-ui.card.title>
                    </x-ui.card.header>
                    <x-ui.card.content class="space-y-4">

                        {{-- Plan row --}}
                        <div class="flex items-center justify-between gap-2">
                            <div>
                                <p class="font-medium text-foreground">{{ $planName }}</p>
                                <p class="text-sm text-muted-foreground" x-text="cycle + ' {{ __('billing') }}'">{{ __('Monthly billing') }}</p>
                            </div>
                            <p class="tabular-nums font-semibold text-foreground">
                                $<span x-text="price">{{ $monthlyPrice }}</span><span x-text="suffix">/mo</span>
                            </p>
                        </div>

                        <x-ui.separator />

                        {{-- Tax --}}
                        <div class="flex items-center justify-between gap-2 text-sm text-muted-foreground">
                            <span>{{ __('Taxes') }}</span>
                            <span>{{ __('Calculated at checkout') }}</span>
                        </div>

                        <x-ui.separator />

                        {{-- Total --}}
                        <div class="flex items-center justify-between gap-2">
                            <span class="font-semibold text-foreground">{{ __('Total') }}</span>
                            <p class="tabular-nums font-bold text-foreground">
                                $<span x-text="price">{{ $monthlyPrice }}</span><span x-text="suffix">/mo</span>
                            </p>
                        </div>

                        {{-- Disclosure --}}
                        <p class="text-xs text-muted-foreground" x-text="disclosure">
                            {{ __("You'll be charged $:price every month. Cancel anytime.", ['price' => $monthlyPrice]) }}
                        </p>

                    </x-ui.card.content>
                    <x-ui.card.footer class="flex items-center gap-2 border-t border-border pt-4 text-xs text-muted-foreground">
                        <svg class="size-4 shrink-0 text-muted-foreground" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
                            <rect width="11" height="11" x="3" y="11" rx="2" ry="2" />
                            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
                        </svg>
                        {{ __('Secure checkout') }}
                    </x-ui.card.footer>
                </x-ui.card>
            </div>

        </div>
    </div>
</section>

Ownership & lifecycle

Owner, release state, review evidence and adoption for this item.
Owner
Platform UI (@JoshJML)
Current version
1.1.1
Status
Stable
License
open
Deprecation
Not deprecated
Contract
v1
Foundation
≥ 1.0.0