Directives

On this page 30

stx directives are special template attributes that provide powerful functionality for your templates. This guide covers all available directives and their usage.

Core Directives

Conditional Rendering

@if, v-if, and :if are the same conditional in different syntax (v-if is sugar for @if; x-if for :if). What makes a conditional reactive is the data it reads, not the keyword: a condition that reads a signal re-evaluates on change (including @if/@else chains on a signals page); a condition over server data renders once. See Conditionals for the full breakdown.

@if / @else / @elseif

@if(user.isAdmin)
  <admin-panel />
@elseif(user.isModerator)
  <moderator-panel />
@else
  <user-panel />
@endif

@unless

The opposite of @if:

@unless(user.isVerified)
  <verify-email-banner />
@endunless

@show / @hide

Shorthand for visibility control:

<div @show="isVisible">Visible content</div>
<div @hide="isHidden">Hidden when true</div>

Loops and Iteration

@foreach

Iterate over arrays and objects:

<ul>
  @foreach(users as user)
    <li>{{ user.name }}</li>
  @endforeach
</ul>

<!-- With index -->
<ul>
  @foreach(users as user, index)
    <li>{{ index + 1 }}. {{ user.name }}</li>
  @endforeach
</ul>

<!-- With empty state -->
@foreach(items as item)
  <item-card :data="item" />
@empty
  <p>No items found</p>
@endforeach

@for

Traditional for loop:

@for(let i = 0; i < 5; i++)
  <span>Item {{ i + 1 }}</span>
@endfor

@while

While loop:

@while(condition)
  <keep-trying />
@endwhile

Layout Control

@extends

Inherit from a layout:

@extends('layouts/main')

@section / @yield

Define and render content sections:

<!-- Layout -->
<title>@yield('title')</title>
<main>@yield('content')</main>

<!-- Page -->
@section('title', 'My Page')
@section('content')
  <h1>Welcome</h1>
@endsection

@include

Include other templates:

@include('partials/header')
@include('components/card', { title: 'My Card' })
@includeIf('optional/feature')
@includeWhen(user.isAdmin, 'admin/tools')

Component Directives

@component

Define a component:

@ts
interface ButtonProps {
  type: 'primary' | 'secondary'
  size?: 'sm' | 'md' | 'lg'
}
@endts

@component('Button', {
  props: {
    type: 'primary',
    size: 'md'
  }
})
  <button class="btn btn-{{ type }} btn-{{ size }}">
    <slot></slot>
  </button>
@endcomponent

@slot

Define named slots:

@component('Card')
  <slot name="header"></slot>
  <slot></slot>
  <slot name="footer"></slot>
@endcomponent

<!-- Usage -->
<Card>
  @slot('header')
    <h2>Card Title</h2>
  @endslot

  <p>Card content</p>

  @slot('footer')
    <button>Action</button>
  @endslot
</Card>

Event Handling

@click

Handle click events:

<button @click="handleClick">Click Me</button>
<button @click="count++">Increment</button>

@submit

Handle form submissions:

<form @submit.prevent="handleSubmit">
  <!-- form fields -->
</form>

Event Modifiers

Add event modifiers with dots:

<button @click.stop="handleClick">Stop Propagation</button>
<button @click.prevent="handleClick">Prevent Default</button>
<form @submit.prevent="handleSubmit">Submit</form>
<div @click.once="handleClick">Click Once Only</div>
<button @click.self="handleClick">Self Only</button>

Data Binding

@bind

Two-way data binding:

<input @bind="username" />
<select @bind="selectedOption">
  <option value="1">One</option>
  <option value="2">Two</option>
</select>

@model

Form input binding:

<input type="text" @model="user.name" />
<input type="checkbox" @model="isChecked" />
<select @model="selected">
  <!-- options -->
</select>

Advanced Directives

@memo

Memoize expensive computations:

@memo(expensiveValue)
  <heavy-component x-value="expensiveValue" />
@endmemo

@watch

Watch for changes:

@watch(user.status)
  <status-indicator :status="user.status" />
@endwatch

@error

Error handling:

@error
  <div class="error-message">
    {{ error.message }}
  </div>
@enderror

Custom Directives

Create your own directives:

// directives/tooltip.ts
import { defineDirective } from '@stacksjs/stx'

export const tooltip = defineDirective({
  mounted(el, { value }) {
    el.setAttribute('title', value)
  },
  updated(el, { value }) {
    el.setAttribute('title', value)
  }
})

// Usage in template
<button @tooltip="Help text">Hover me</button>

Best Practices

  1. Conditional Rendering

    • Use @if for simple conditions
    • Use @switch for multiple conditions
    • Consider extracting complex logic to computed properties
  2. Loops

    • Always provide a key for list items
    • Use @empty for empty states
    • Consider pagination for large lists
  3. Event Handling

    • Use appropriate event modifiers
    • Keep event handlers simple
    • Consider debouncing/throttling when needed
  4. Component Organization

    • Keep directives focused and reusable
    • Document custom directives
    • Test directive behavior

Next Steps