RK

Search products

Search plugins, packages and apps, or jump to a section.

NativePHP UI

shadcn/ui - styled native components for NativePHP Mobile SuperNative screens.

NativePHP Plugin
iOSAndroid
v1.0.10 downloads

NativePHP UI

shadcn/ui for NativePHP Mobile SuperNative screens.

Write <ui:button>, <ui:card>, <ui:tabs> in your NativeComponent views and get shadcn's look — its colors, spacing, radii and dark mode — rendered as real SwiftUI and Jetpack Compose views, not HTML in a web view.

<ui:page>
    <ui:card>
        <ui:card-header>
            <ui:card-title>Create project</ui:card-title>
            <ui:card-description>Deploy your new project in one click.</ui:card-description>
        </ui:card-header>
        <ui:card-content>
            <ui:field label="Name">
                <ui:input native:model="name" placeholder="Name of your project" />
            </ui:field>
            <ui:switch native:model="private">Private repository</ui:switch>
        </ui:card-content>
        <ui:card-footer>
            <ui:button variant="outline" @press="cancel">Cancel</ui:button>
            <ui:button class="flex-1" :loading="$deploying" @press="deploy">Deploy</ui:button>
        </ui:card-footer>
    </ui:card>
</ui:page>
  • 30+ components, 50+ tags
  • shadcn base colors (neutral, zinc, slate, stone, gray) and accent themes (blue, green, orange, red, rose, violet, yellow)
  • Automatic light / dark mode
  • Configurable radius, like shadcn's --radius
  • Two-way binding with native:model
  • No native code, no rebuild for updates — it's pure PHP on top of NativePHP's EDGE elements

Requirements

  • NativePHP Mobile v4.5+ with SuperNative (NativeComponent) screens
  • nativephp/mobile-ui 0.8+ (installed automatically)
  • Android 8.0 (API 26)+, iOS 18.2+, the same minimums as nativephp/mobile-ui

Installation

Purchase a license on the CodingwithRK Marketplace, add the marketplace repository it gives you to your composer.json, then:

composer require codingwithrk/nativephp-ui
php artisan native:plugin:register codingwithrk/nativephp-ui

Optionally publish the config:

php artisan vendor:publish --tag=nativephp-ui-config

AI skill (Claude Code & Codex)

The package ships an agent skill that teaches AI coding agents how to build screens with these components: every tag and attribute, binding, theming, testing, ready-made screen recipes and common mistakes.

  • With Laravel Boost: run php artisan boost:install and enable Agent Skills. Boost picks the skill up from this package and installs it for every agent you use.

  • Without Boost:

    php artisan nativephp-ui:skill            # Claude Code (.claude/skills) and Codex (.agents/skills)
    php artisan nativephp-ui:skill --claude   # only Claude Code
    php artisan nativephp-ui:skill --codex    # only Codex
    php artisan nativephp-ui:skill --force    # overwrite after upgrading the package
    

How it works

<ui:*> tags are expanded at view-compile time into ordinary <native:*> EDGE markup (<native:row>, <native:text>, <native:icon>, …) styled with shadcn's tokens. So everything you know about EDGE still applies:

  • Your classes win. A class on a <ui:*> tag is applied after the component's own, so <ui:button class="h-12 w-full"> just works.
  • Unknown attributes are forwarded to the native element: ref, a11y-label, @navigate.fade='/next', @longPress, native:key, and so on.
  • Props can be dynamic: :variant="$danger ? 'destructive' : 'default'". Use single-quoted strings inside :prop expressions.
  • <ui:*> tags only compile inside native views. Web/Livewire views are left untouched.

Theming

config/nativephp-ui.php:

'base_color' => 'zinc',   // neutral | zinc | slate | stone | gray
'accent'     => 'violet', // null | blue | green | orange | red | rose | violet | yellow
'radius'     => 10,       // sm = r-4, md = r-2, lg = r, xl = r+4
'tokens'     => [
    'light' => ['primary' => '#4F46E5'],
    'dark'  => ['primary' => '#818CF8'],
],

Every token is available to your own markup as a theme class: bg-theme-ui-card, text-theme-ui-muted-foreground, border-theme-ui-border, …

Tokens: background foreground card card-foreground popover popover-foreground primary primary-foreground secondary secondary-foreground muted muted-foreground accent accent-foreground destructive destructive-foreground border input ring.

They are prefixed ui- and merged into native-ui's theme, so your app's own Material-style tokens (primary, surface, …) are untouched.

Binding state

Stateful components bind the same way native inputs do:

{{-- Two-way: reads and writes $notifications --}}
<ui:switch native:model="notifications" />

{{-- Controlled: reads $wifi, calls setWifi(bool $on) --}}
<ui:switch :checked="$wifi" @change="setWifi" />

Writes go through the same __syncProperty path as native:model, so updatedNotifications() hooks and #[Locked] behave as you'd expect.

Component Controlled prop
switch, checkbox :checked
toggle :pressed
radio-group, tabs, toggle-group, accordion :value
dialog, sheet, drawer :open (handler: @close)

Components

Layout & typography

<ui:page>…</ui:page>                     {{-- scrolling screen root; :scroll="false" for a fixed one --}}
<ui:h1>Heading</ui:h1>   {{-- also ui:h2, ui:h3, ui:h4 --}}
<ui:p>Body text</ui:p>   {{-- also ui:lead, ui:large, ui:small, ui:muted, ui:code --}}
<ui:separator />  <ui:separator orientation="vertical" />

Button

<ui:button @press="save">Save</ui:button>
<ui:button variant="outline" size="sm" icon="plus">Add</ui:button>
<ui:button variant="ghost" size="icon" icon="more_horiz" a11y-label="More" />
<ui:button variant="destructive" :disabled="$locked" :loading="$deleting" @press="delete">Delete</ui:button>
<ui:button variant="link" @navigate='/terms'>Terms</ui:button>

Variants: default secondary destructive outline ghost link. Sizes: default sm lg icon icon-sm icon-lg. While disabled or loading, the press handler is removed.

Badge, Kbd, Spinner, Avatar, Skeleton, Progress

<ui:badge>New</ui:badge>  <ui:badge variant="secondary|destructive|outline" icon="check">Verified</ui:badge>
<ui:kbd>⌘K</ui:kbd>
<ui:spinner />
<ui:avatar :src="$user->photo" :fallback="$user->initials" size="sm|default|lg|xl" />
<ui:skeleton class="h-4 w-[220]" />
<ui:progress :value="$percent" />

Card

<ui:card>
    <ui:card-header>
        <ui:card-title>Notifications</ui:card-title>
        <ui:card-description>You have 3 unread messages.</ui:card-description>
        <ui:card-action><ui:button size="sm" variant="outline">View</ui:button></ui:card-action>
    </ui:card-header>
    <ui:card-content>…</ui:card-content>
    <ui:card-footer>…</ui:card-footer>
</ui:card>

Alert

<ui:alert icon="info">
    <ui:alert-title>Heads up!</ui:alert-title>
    <ui:alert-description>You can add components to your app.</ui:alert-description>
</ui:alert>
<ui:alert variant="destructive" icon="warning">…</ui:alert>

Forms

<ui:field label="Email" description="We'll never share it." :error="$emailError">
    <ui:input native:model="email" keyboard="email" icon="mail" :invalid="filled($emailError)" />
</ui:field>

<ui:label>Bio</ui:label>
<ui:textarea native:model="bio" placeholder="Tell us about yourself" />

<ui:switch native:model="marketing">Marketing emails</ui:switch>
<ui:checkbox native:model="terms">Accept terms and conditions</ui:checkbox>

<ui:radio-group native:model="plan">
    <ui:radio-group-item value="free">Free</ui:radio-group-item>
    <ui:radio-group-item value="pro">Pro</ui:radio-group-item>
</ui:radio-group>

<ui:input> and <ui:textarea> forward everything to NativePHP's native text input: placeholder, secure, revealable, keyboard, max-length, @submit, native:model.debounce.300ms, …

Tabs

<ui:tabs native:model="tab">
    <ui:tabs-list>
        <ui:tabs-trigger value="account">Account</ui:tabs-trigger>
        <ui:tabs-trigger value="password">Password</ui:tabs-trigger>
    </ui:tabs-list>
    <ui:tabs-content value="account">…</ui:tabs-content>
    <ui:tabs-content value="password">…</ui:tabs-content>
</ui:tabs>

Only the active panel is rendered.

Toggle & toggle group

<ui:toggle native:model="bold" icon="format_bold" a11y-label="Bold" />

<ui:toggle-group type="single" native:model="align" variant="outline">
    <ui:toggle-group-item value="left" icon="format_align_left" a11y-label="Left" />
    <ui:toggle-group-item value="center" icon="format_align_center" a11y-label="Center" />
</ui:toggle-group>

type="multiple" binds an array.

Accordion

<ui:accordion native:model="faq">
    <ui:accordion-item value="shipping" title="Is shipping free?">Yes, on orders over $50.</ui:accordion-item>
    <ui:accordion-item value="returns" title="Can I return it?">Within 30 days.</ui:accordion-item>
</ui:accordion>

type="multiple" lets several items stay open at once.

Dialog, Sheet, Drawer

These are presented as a native bottom sheet, which is how shadcn itself shows dialogs on mobile.

<ui:dialog native:model="confirming">
    <ui:dialog-header>
        <ui:dialog-title>Delete project?</ui:dialog-title>
        <ui:dialog-description>This action cannot be undone.</ui:dialog-description>
    </ui:dialog-header>
    <ui:dialog-footer>
        <ui:button variant="destructive" @press="delete">Delete</ui:button>
        <ui:button variant="outline" @press="cancel">Cancel</ui:button>
    </ui:dialog-footer>
</ui:dialog>

<ui:sheet :open="$filtersOpen" @close="closeFilters" detents="medium,large">…</ui:sheet>

Swiping the sheet away sets the bound property back to false (or calls @close). Other attributes, such as detents, go to the native bottom sheet.

Item & Empty

<ui:item-group>
    <ui:item icon="person" title="Profile" description="Name, photo, bio" chevron @navigate='/profile' />
    <ui:item variant="outline" title="Two-factor authentication">
        <ui:switch native:model="twoFactor" />
    </ui:item>
</ui:item-group>

<ui:empty icon="folder" title="No projects yet" description="Create your first project to get started.">
    <ui:button @press="create">Create project</ui:button>
</ui:empty>

Custom components

Register your own <ui:*> tag from a service provider. Extend Codingwithrk\NativephpUi\Components\Component, return native markup from render(Tag $tag) and classes from classes():

use Codingwithrk\NativephpUi\Ui;

Ui::register('stat', \App\Ui\Stat::class);

Testing

Components render through NativePHP's own test harness:

Native::test(Settings::class)
    ->tap('dark-mode')            // ref="dark-mode" on <ui:switch>
    ->assertSet('darkMode', true);

License

Commercial. See LICENSE. © CodingwithRK.

We only use essential cookies to keep you signed in, remember your cart and protect forms. No tracking or ads. Learn more