RizzyUI

Plugins & Directives

RizzyUI isn't just a set of components; it's an interactive toolkit. We've pre-packaged some of the most useful official Alpine.js plugins and added our own custom directives to solve common UI challenges right out of the box.

Official Alpine.js Plugins

Collapse (x-collapse)

This plugin smoothly animates an element's height from 0 to its full height. It's the magic behind components like <RzAccordion> and <RzCollapsible>.

Source
<div x-data="{ open: false }">
  <RzButton x-on:click="open = !open" :aria-expanded="open">
    Toggle Content
  </RzButton>

  <div x-show="open" x-collapse class="mt-2 border p-4 rounded-md">
    This content will slide open and closed.
  </div>
</div>

Note: x-collapse only animates height. For a fade-and-slide effect, combine it with x-transition.

Intersect (x-intersect)

This plugin detects when an element enters or leaves the viewport. It's perfect for triggering animations, lazy-loading images, or firing analytics events. Use modifiers like .once to control how often it fires.

Source
<div x-data="{ isVisible: false }"
     x-intersect.once:enter="isVisible = true"
     :class="{ 'opacity-100 translate-y-0': isVisible, 'opacity-0 translate-y-4': !isVisible }"
     class="transition-all duration-500 ease-out">
  <RzCard>
    <CardContent>This card fades in only the first time it becomes visible.</CardContent>
  </RzCard>
</div>

Focus (x-trap)

This is a critical plugin for accessibility. It "traps" the user's focus within an element, such as a modal dialog. This prevents users from accidentally tabbing to elements in the background.

Source
<div x-data="{ isOpen: false }">
  <RzButton x-on:click="isOpen = true">Open Dialog</RzButton>

  <template x-teleport="body">
    <div x-show="isOpen" class="modal-backdrop">
      <div x-show="isOpen" x-trap.inert="isOpen" role="dialog" aria-modal="true" aria-labelledby="dialog-title" class="modal-dialog">
        <h3 id="dialog-title">Dialog Title</h3>
        <p>Focus is trapped inside this dialog. Try tabbing!</p>
        <RzButton x-on:click="isOpen = false">Close</RzButton>
      </div>
    </div>
  </template>
</div>

Note: RizzyUI’s <RzDialog> and <RzSheet> components already use x-trap internally. You only need to use it yourself when building custom modal-like components.

RizzyUI Custom Directives

x-mobile: Simple Responsive Logic

The x-mobile directive detects if the viewport is below a breakpoint (default: 768px) and updates automatically on resize. It provides two ways to react:

  • CSS Attributes: It automatically adds data-screen="mobile" or data-screen="desktop" to the element.
  • Alpine State: It can sync a boolean value to your component's state.
Source
<!-- Customize the breakpoint with a modifier -->
<div x-data="{ isDesktop: false }" x-mobile.bp-1024="isDesktop">
  <div :class="{ 'flex-col': !isDesktop, 'flex-row': isDesktop }" class="flex gap-4">
    <!-- This layout is vertical below 1024px and horizontal above -->
  </div>

  <p>
    Current view: <span x-text="isDesktop ? 'Desktop' : 'Mobile'"></span>
  </p>
</div>

x-syncprop: Two-Way State Synchronization

The x-syncprop directive creates a two-way binding between a property in a parent Alpine component and a property in a **direct child** component.

The syntax is x-syncprop="parent.property -> child.property". You can sync multiple properties by separating them with a comma.

Syncing Multiple Properties
<div x-data="{ form: { name: 'Alice', email: 'alice@example.com' } }">
  <!-- Child component that syncs with the parent form object -->
  <div x-data="{ localName: '', localEmail: '' }"
       x-syncprop="form.name -> localName, form.email -> localEmail">
    
    <label>Name:</label>
    <input type="text" x-model="localName" class="input" />

    <label class="mt-2">Email:</label>
    <input type="email" x-model="localEmail" class="input" />
  </div>
</div>

Controlling Initial Sync Direction

By default, the parent's value overwrites the child's. You can reverse this by adding the .init-child modifier.

Source
<div x-data="{ form: { age: 30 } }">
  <!-- Here, the child's initial value of 99 will overwrite the parent's value of 30 -->
  <div x-data="{ fields: { ageCopy: 99 } }"
       x-syncprop.init-child="form.age -> fields.ageCopy">
  </div>
</div>

Best Practices

  • x-collapse: Use for vertical slide animations. For a fade-and-slide effect, combine it with x-transition.
  • x-intersect: Prefer using the .once modifier for animations that should only happen once to avoid re-triggering on scroll bounce.
  • x-trap: When building custom modals, always pair x-trap with role="dialog" and aria-modal="true" for proper accessibility.
  • x-mobile: The directive listens for `resize` events. Avoid putting expensive DOM updates in a watcher that observes the synced boolean to prevent performance issues.
  • x-syncprop: Only sync the properties you need. Avoid syncing large, deeply nested objects unnecessarily to keep reactivity efficient.

Next Steps

You’ve now seen how to enhance individual components with plugins and directives. Next, we’ll look at orchestrating multiple components together, where helpers like $refs and Rizzy.$data() really shine.

Let's move on to Advanced: Orchestrating Components.