Current Plan
A compact current-plan card for plan name, billing interval, renewal or cancellation state, bounded usage meters, and an application-owned management link.
Preview
Current plan
Pro
For teams that ship every week.
$29 per month (billed yearly)
- Renews
- Payment method
- Visa ending 4242
Usage
Included
- Unlimited projects
- Priority support
- SSO and audit log
- 90-day history
@php
$usage = [
['label' => __('Team members'), 'used' => 7, 'limit' => 10],
['label' => __('Storage'), 'used' => 84, 'limit' => 100, 'unit' => 'GB'],
['label' => __('API requests'), 'used' => 120000, 'limit' => 500000],
];
$features = [__('Unlimited projects'), __('Priority support'), __('SSO and audit log'), __('90-day history')];
@endphp
<x-ui.current-plan
class="mx-auto w-full max-w-md"
name="Pro"
description="{{ __('For teams that ship every week.') }}"
price="$29"
price-note="{{ __('per month') }}"
interval="{{ __('billed yearly') }}"
state="active"
:renews-at="now()->addYear()"
payment-method="{{ __('Visa ending 4242') }}"
:usage="$usage"
:features="$features"
action-url="#"
secondary-action-url="#"
/>
Installation
php artisan ui:add current-plan
Registry contract
php artisan ui:add current-plan
writes only the files below. The CLI validates each file hash before writing and asks before it replaces a local change, unless you pass --force.
-
resources/views/components/ui/current-plan.blade.php
Use with AI
A brief for your coding agent: install command, usage, props, guidance and the rules. Copy it, or open a prompt about this component in an assistant.
# Brok UI: Current Plan (`current-plan`)
A compact current-plan card for plan name, billing interval, renewal or cancellation state, bounded usage meters, and an application-owned management link.
Brok UI is a Laravel Blade component registry. Installed components are plain Blade files the app owns.
## Install
```bash
php artisan ui:add current-plan
```
## Usage
```blade
@php
$usage = [
['label' => __('Team members'), 'used' => 7, 'limit' => 10],
['label' => __('Storage'), 'used' => 84, 'limit' => 100, 'unit' => 'GB'],
['label' => __('API requests'), 'used' => 120000, 'limit' => 500000],
];
$features = [__('Unlimited projects'), __('Priority support'), __('SSO and audit log'), __('90-day history')];
@endphp
<x-ui.current-plan
class="mx-auto w-full max-w-md"
name="Pro"
description="{{ __('For teams that ship every week.') }}"
price="$29"
price-note="{{ __('per month') }}"
interval="{{ __('billed yearly') }}"
state="active"
:renews-at="now()->addYear()"
payment-method="{{ __('Visa ending 4242') }}"
:usage="$usage"
:features="$features"
action-url="#"
secondary-action-url="#"
/>
```
## Props
- `name` (string, default `required`) — Current plan name.
- `interval` (string|null, default `null`) — Server-formatted billing interval.
- `state` (active|trial|past_due|cancelled, default `active`) — Billing state for presentation.
- `renewsAt` (instant|null, default `null`) — Renewal or access-end instant.
- `usage` (list<UsageMeter>, default `[]`) — Up to eight usage meters: label, used, limit and an optional unit (GB). A meter turns warning at 80% and destructive at the limit.
- `actionUrl` (url|null, default `null`) — Application-owned plan management URL.
- `actionLabel` (string, default `Manage plan`) — Localized plan management label.
- `price` (string|null, default `null`) — Server-formatted price shown as the hero figure.
- `priceNote` (string|null, default `null`) — What the price is per, shown after it (per month).
- `description` (string|null, default `null`) — One line under the plan name.
- `features` (list<string>, default `[]`) — Up to eight included items, shown as a check list.
- `paymentMethod` (string|null, default `null`) — Server-formatted payment method (Visa ending 4242).
- `secondaryActionUrl` (url|null, default `null`) — A second, outline action (change plan, cancel).
- `secondaryActionLabel` (string, default `Change plan`) — Label of the second action.
## Use when
- Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
- Summarize subscription data that the billing backend already calculated.
## Avoid when
- Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
- Comparing multiple plans side by side; use blocks/comparison-plans-table instead of a single plan summary.
- Do not use this component to calculate invoices, renewals, or entitlements.
## Anti-patterns
- Using cards for dense cross-item comparison
## Rules
- Use the `<brok:current-plan>` tag (or `<x-ui.current-plan>`) in Blade; do not rewrite the component.
- Prefer the documented props and variants over utility-class overrides; when a utility must win, use the `!` important modifier.
- Use semantic design tokens (`bg-primary`, `text-muted-foreground`), never raw colour utilities.
- Keep the `data-slot` attributes; they are the styling and test hooks.
## Links
- Docs: https://brokui.dev/docs/components/current-plan
- Registry JSON (files, props, contract): https://brokui.dev/r/open/current-plan.json
Working in Claude Code, Cursor or Codex? Give the agent the whole registry through the MCP server or the Brok UI skill.
Examples
Long Content
<div class="max-w-xs">
<x-ui.current-plan name="{{ __('Enterprise collaboration and governance plan with a long translated name') }}" interval="{{ __('Billed once for permanent access') }}" :usage="[['label' => __('A long usage meter label'), 'used' => 42, 'limit' => 100]]" />
</div>
<div class="grid gap-4 md:grid-cols-2">
<x-ui.current-plan name="Pro trial" interval="14 days" state="trial" :renews-at="now()->addDays(8)" />
<x-ui.current-plan name="Pro" interval="Yearly" state="cancelled" :renews-at="now()->addDays(21)" />
</div>
API
Props
| Prop | Type | Default | Description |
|---|---|---|---|
| name | string | required | Current plan name. |
| interval | string | null | null | Server-formatted billing interval. |
| state | active | trial | past_due | cancelled | active | Billing state for presentation. |
| renewsAt | instant | null | null | Renewal or access-end instant. |
| usage | list<UsageMeter> | [] | Up to eight usage meters: label, used, limit and an optional unit (GB). A meter turns warning at 80% and destructive at the limit. |
| actionUrl | url | null | null | Application-owned plan management URL. |
| actionLabel | string | Manage plan | Localized plan management label. |
| price | string | null | null | Server-formatted price shown as the hero figure. |
| priceNote | string | null | null | What the price is per, shown after it (per month). |
| description | string | null | null | One line under the plan name. |
| features | list<string> | [] | Up to eight included items, shown as a check list. |
| paymentMethod | string | null | null | Server-formatted payment method (Visa ending 4242). |
| secondaryActionUrl | url | null | null | A second, outline action (change plan, cancel). |
| secondaryActionLabel | string | Change plan | Label of the second action. |
Slots
Default Blade slot only.
Data slots
Stable hooks for CSS overrides and browser tests.
Behavior
- Presents billing view data without owning billing logic.
- Declares registry capability flags: a11y, authoredStateFixtures, responsive, rtl, darkMode, localized.
Guidance
Present a self-contained content object with local context.
Use when
- Use when self-contained content objects benefit from a recognizable card-like presentation with local actions or metadata.
- Summarize subscription data that the billing backend already calculated.
Avoid when
- Do not use card widgets where a simpler list, table, or plain text block would make comparison faster.
- Comparing multiple plans side by side; use blocks/comparison-plans-table instead of a single plan summary.
- Do not use this component to calculate invoices, renewals, or entitlements.
Use instead
- List for compact scanning
- Table for exact comparison
Anti-patterns
- Using cards for dense cross-item comparison
- Anatomy
- Theming hooks
Accessibility
- Keyboard
- No component-owned keyboard interaction; native element behavior applies.
- Focus
none
- Plan state is visible text.
- Usage meters expose accessible labels and values.
- Semantic HTML and a stable
data-slotattribute for styling and scripting hooks. - Focus-visible rings use the
ringtoken, so keyboard focus is always visible. - Disabled and invalid states are conveyed to assistive tech, not by color alone.
- Targets WCAG 2.2 AA; verify contrast in light, dark, admin and customer surfaces in the preview.
- Labels go through
__()and layout uses logical properties (ms-*,text-start), so it mirrors underdir="rtl"— flip the preview to RTL to confirm. - Dark mode uses the same semantic tokens under the
darkclass; high contrast follows forced-color system tokens.
Livewire
Livewire can update this component through forwarded wire:* attributes.
Source
The exact, editable file ui:add writes
into your app. Previews render this same code; there are no preview-only components.
@props([
'name',
'interval' => null,
'state' => 'active',
'renewsAt' => null,
'usage' => [],
'actionUrl' => null,
'actionLabel' => 'Manage plan',
// Server-formatted price ("$29") and what it is per ("per month").
'price' => null,
'priceNote' => null,
// One line under the name: what the plan is for.
'description' => null,
// What the plan includes, shown as a check list.
'features' => [],
// Server-formatted payment method ("Visa ending 4242").
'paymentMethod' => null,
// A second, quieter action (change plan, cancel).
'secondaryActionUrl' => null,
'secondaryActionLabel' => 'Change plan',
])
@php
$stateVariants = ['active' => 'success', 'trial' => 'info', 'past_due' => 'warning', 'cancelled' => 'secondary'];
$stateLabels = ['active' => __('Active'), 'trial' => __('Trial'), 'past_due' => __('Past due'), 'cancelled' => __('Cancelled')];
$resolvedState = array_key_exists($state, $stateVariants) ? $state : 'active';
$usage = array_slice(array_values(array_filter((array) $usage, 'is_array')), 0, 8);
$features = array_slice(array_values(array_filter((array) $features, 'is_string')), 0, 8);
@endphp
{{-- Current plan: the plan, its price and state, when it renews and how it
is paid, what it includes, the usage meters (a meter turns warning at
80% and destructive at the limit) and the plan actions. --}}
<x-ui.card data-slot="current-plan" data-state="{{ $resolvedState }}" {{ $attributes }}>
<x-ui.card.header class="flex-row flex-wrap items-start justify-between gap-3">
<div class="min-w-0">
<x-ui.card.description>{{ __('Current plan') }}</x-ui.card.description>
<x-ui.card.title class="truncate">{{ $name }}</x-ui.card.title>
@if (filled($description))
<p class="mt-1 text-sm text-muted-foreground">{{ $description }}</p>
@endif
</div>
<x-ui.badge :variant="$stateVariants[$resolvedState]" dot>{{ $stateLabels[$resolvedState] }}</x-ui.badge>
</x-ui.card.header>
<x-ui.card.content class="space-y-6">
@if (filled($price) || filled($renewsAt) || filled($paymentMethod))
<div class="flex flex-wrap items-end justify-between gap-3">
@if (filled($price))
<p class="flex items-baseline gap-1">
<span data-slot="current-plan-price" class="text-2xl font-semibold tabular-nums tracking-tight">{{ $price }}</span>
@if (filled($priceNote))<span class="text-sm text-muted-foreground">{{ $priceNote }}</span>@endif
@if (filled($interval))<span class="text-sm text-muted-foreground">({{ $interval }})</span>@endif
</p>
@endif
<dl class="flex flex-col items-end gap-1 text-sm text-muted-foreground">
@if (filled($renewsAt))
<div class="flex items-center gap-2">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2" /><path d="M16 2v4M8 2v4M3 10h18" /></svg>
<dt>{{ $resolvedState === 'cancelled' ? __('Access ends') : __('Renews') }}</dt>
<dd class="text-foreground"><x-ui.relative-time :datetime="$renewsAt" mode="date" /></dd>
</div>
@endif
@if (filled($paymentMethod))
<div class="flex items-center gap-2">
<svg class="size-4 shrink-0" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="5" width="20" height="14" rx="2" /><path d="M2 10h20" /></svg>
<dt class="sr-only">{{ __('Payment method') }}</dt>
<dd class="text-foreground">{{ $paymentMethod }}</dd>
</div>
@endif
</dl>
</div>
@endif
@if ($usage !== [])
<div class="space-y-4">
<p class="text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{{ __('Usage') }}</p>
@foreach ($usage as $meter)
@php
$used = min(1000000000, max(0, (int) ($meter['used'] ?? 0)));
$limit = min(1000000000, max(0, (int) ($meter['limit'] ?? 0)));
$value = $limit > 0 ? min(100, (int) floor(($used * 100) / $limit)) : 0;
$unit = (string) ($meter['unit'] ?? '');
$tone = $value >= 100 ? 'destructive' : ($value >= 80 ? 'warning' : 'default');
@endphp
<div class="space-y-2" data-slot="current-plan-meter" data-tone="{{ $tone }}">
<div class="flex min-w-0 items-baseline justify-between gap-3 text-sm">
<span class="truncate text-foreground">{{ $meter['label'] ?? __('Usage') }}</span>
<span class="shrink-0 tabular-nums text-muted-foreground"><span class="font-medium text-foreground">{{ number_format($used) }}</span> / {{ number_format($limit) }}{{ $unit !== '' ? ' '.$unit : '' }}</span>
</div>
<x-ui.progress :value="$value" :tone="$tone" class="h-2" :aria-label="$meter['label'] ?? __('Usage')" />
</div>
@endforeach
</div>
@endif
@if ($features !== [])
<div class="space-y-2">
<p class="text-2xs font-semibold uppercase tracking-wide text-muted-foreground">{{ __('Included') }}</p>
<ul class="grid gap-2 text-sm sm:grid-cols-2" role="list">
@foreach ($features as $feature)
<li class="flex gap-2">
<svg class="mt-px size-4 shrink-0 text-success" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m5 12 5 5L20 7" /></svg>
<span class="min-w-0">{{ $feature }}</span>
</li>
@endforeach
</ul>
</div>
@endif
</x-ui.card.content>
@if (filled($actionUrl) || filled($secondaryActionUrl))
<x-ui.card.footer class="flex-wrap gap-2 border-t border-border pt-6">
@if (filled($actionUrl))
<x-ui.button as="a" :href="$actionUrl">{{ __($actionLabel) }}</x-ui.button>
@endif
@if (filled($secondaryActionUrl))
<x-ui.button as="a" :href="$secondaryActionUrl" variant="outline">{{ __($secondaryActionLabel) }}</x-ui.button>
@endif
</x-ui.card.footer>
@endif
</x-ui.card>
Ownership & lifecycle
Owner, release state, review evidence and adoption for this item.
- Owner
- Platform UI (@JoshJML)
- Current version
-
1.1.2 - Status
- Stable
- License
-
open - Accessibility reviewed
- No review date recorded
- Last breaking change
- No date recorded
- Deprecation
- Not deprecated
- Contract
-
v6 - Foundation
-
≥ 1.0.0