Complete Reference

FyneJS Directives

Complete reference guide to all FyneJS directives. Build reactive UIs with simple, declarative attributes.

16 Directives
30+ Event Modifiers
100% Declarative

Core Directives

Essential directives for creating reactive components and managing data flow.

x-data Component Initialization

Defines a reactive component with initial data. The root directive that makes elements reactive.

<div x-data="{ count: 0, message: 'Hello World' }">
  <span x-text="message"></span>
  <button x-on:click="count++">Count: <span x-text="count"></span></button>
</div>

x-text Text Content Binding

Sets the text content of an element. Automatically escapes HTML for security.

<div x-data="{ name: 'FyneJS', version: '1.0' }">
  <h1 x-text="name"></h1>
  <p x-text="`Version: ${version}`"></p>
</div>

\{{ ... }} Mustache Text Interpolation

Embed values inline without attributes. Escapes HTML by default.

<div x-data="{ first: 'Ada', last: 'Lovelace' }">
  <p>Hello, \{{ first }} \{{ last }}!</p>
</div>

x-html HTML Content Binding

Sets the innerHTML of an element. Use with caution as it doesn't escape HTML.

<div x-data="{ content: '<strong>Bold text</strong>' }">
  <div x-html="content"></div>
</div>

x-show Toggle Visibility

Shows or hides an element using CSS display property. Element remains in DOM.

<div x-data="{ visible: true }">
  <button x-on:click="visible = !visible">Toggle</button>
  <p x-show="visible">This text can be toggled!</p>
</div>

x-transition Transitions (enter/leave + end hooks)

Animate visibility changes with class-based phases or a simple toggle class list. Works with both x-show and x-if.

Simple toggle form

<div x-data="{ open: false }">
  <button x-on:click="open = !open">Toggle</button>
  <div
    x-show="open"
    x-transition="opacity-0 transition-opacity duration-200"
    class="p-4 bg-white rounded shadow"
  >Fades in/out using your classes</div>
</div>

On show, the classes are added; on hide, they are removed. The framework waits for the computed CSS transition/animation duration (with delays) before finishing.

Configuration object form

You can pass an object with keys like duration, easing, enter, enterFrom, enterTo, leave, leaveFrom, leaveTo, and toggle. See the TransitionConfig API reference for full details.

<div
  x-show="open"
  x-transition="{ duration: 300, easing: 'ease-out', enter: 'transition', enterFrom: 'opacity-0', enterTo: 'opacity-100' }"
>Dialog</div>

Granular phases (Tailwind-style)

<div
  x-data="{ open: true }"
  x-show="open"
  x-transition:enter="transition ease-out duration-200"
  x-transition:enter-from="opacity-0 -translate-y-2"
  x-transition:enter-to="opacity-100 translate-y-0"
  x-transition:leave="transition ease-in duration-150"
  x-transition:leave-from="opacity-100 translate-y-0"
  x-transition:leave-to="opacity-0 translate-y-2"
>Content</div>

Run code after transition ends

<div
  x-data="{ open: true }"
  x-show="open"
  x-transition:enter="transition ease-out duration-500"
  x-transition:enter-from="opacity-0 translate-x-full"
  x-transition:enter-to="opacity-100 translate-x-0"
  x-transition:enter.after="({ el, phase, config }) => console.log(phase, config.duration, el)"
>Slide-in panel</div>

Use .after (or .end) on x-transition or any phase (e.g., x-transition:enter.after) to run an expression once the transition completes. The handler receives: { el, phase, config } where config.duration is the effective duration (ms) detected from CSS (including delays), and phase is enter or leave.

  • Actual duration is computed from CSS transition/animation durations and delays; falls back only if none are defined.
  • End callbacks do not run on cancellations—only on natural completion—to avoid duplicates.
  • Works with x-if: entering branches animate in; leaving branches animate out and are removed after completion. The original display is restored (including display: contents for template wrappers).

x-if x-else-if x-else Conditional Rendering

Conditionally render elements. Unlike x-show, these directives add/remove elements from DOM.

<div x-data="{ status: 'loading' }">
  <div x-if="status === 'loading'">Loading...</div>
  <div x-else-if="status === 'error'">Error occurred!</div>
  <div x-else>Content loaded successfully</div>
</div>

x-for List Rendering

Renders a list of elements from arrays, objects, iterables, or numeric ranges. Supports complex iteration patterns with stable diffing via x-key.

<div x-data="{ items: ['Apple', 'Banana', 'Cherry'] }">
  <ul>
    <!-- Basic array loop with index -->
    <li x-for="(item, index) in items" :key="index">
      <span x-text="`${index + 1}. ${item}`"></span>
    </li>
  </ul>
</div>

<!-- Numeric range: iterate n times -->
<div x-data>
  <template x-for="star in 5">
    <span>⭐</span>  <!-- Renders 5 stars -->
  </template>
</div>

<!-- Object iteration: (value, key, index) -->
<div x-data="{ user: { name: 'Alice', role: 'Admin', status: 'Active' } }">
  <template x-for="(value, key, index) in user">
    <p><strong x-text="key"></strong>: <span x-text="value"></span></p>
  </template>
</div>

<!-- Advanced: Using x-key for stable diffing with objects -->
<div x-data="{ 
  users: [
    { id: 1, name: 'Alice', email: 'alice@example.com' },
    { id: 2, name: 'Bob', email: 'bob@example.com' }
  ]
}">
  <template x-for="user in users" x-key="user.id">
    <div class="user-card">
      <h3 x-text="user.name"></h3>
      <p x-text="user.email"></p>
    </div>
  </template>
</div>

Supported Iterable Types

Arrays: item in items or (item, index) in items

Objects: (value, key) in obj or (value, key, index) in obj

Numbers: n in 5 iterates 5 times (n = 0, 1, 2, 3, 4)

Iterables: Map, Set, and other iterable objects

x-key for Stable Diffing

Use x-key on your template element to provide stable keys for efficient list updates. This prevents unnecessary DOM recreation when items are reordered or modified.

When working with objects, use unique properties like x-key="item.id" rather than array indices for better performance.

x-model Two-Way Data Binding

Creates two-way data binding for form inputs. Automatically syncs input values with data.

<div x-data="{ name: '', email: '', agree: false }">
  <input x-model="name" placeholder="Your name">
  <input x-model="email" type="email" placeholder="Your email">
  <label><input x-model="agree" type="checkbox"> I agree</label>
  
  <p>Name: <span x-text="name"></span></p>
</div>

x-init Initialization Code

Runs code when the component is initialized. Perfect for setup tasks.

<div x-data="{ time: '' }" 
     x-init="time = new Date().toLocaleTimeString()">
  <p>Page loaded at: <span x-text="time"></span></p>
</div>

x-intersect:enter x-intersect:leave Intersection Observer

Trigger actions when elements enter or leave the viewport using the Intersection Observer API.

<div x-data="{ visible: false, count: 0 }">
  <!-- Trigger when entering viewport -->
  <div x-intersect:enter="visible = true; console.log('Element entered!')"
       class="h-64 bg-blue-100 flex items-center justify-center">
    <span x-text="visible ? 'I am visible!' : 'Scroll to see me'"></span>
  </div>
  
  <!-- Trigger when leaving viewport -->
  <div x-intersect:leave="console.log('Element left viewport')"
       class="h-32 bg-red-100">
    Scroll past me!
  </div>
  
  <!-- With modifiers -->
  <div x-intersect:enter.once.rootMargin-100px="count++"
       class="h-48 bg-green-100">
    <p>Triggered <span x-text="count"></span> times</p>
    <p>(Only once, with 100px margin)</p>
  </div>
</div>

Available Modifiers

.once - Trigger only once

.rootMargin-{pixels}px - Adjust detection area (e.g., .rootMargin-50px)

Use Cases

Perfect for lazy loading images, triggering animations on scroll, infinite scrolling, analytics tracking, and progressive disclosure of content.

Learn more: x-intersect Callback Context for details on the payload passed to your callbacks.

x-ref Element References

Create named references to DOM elements for direct access in your component logic via $refs.

<div x-data="{ focusInput() { $refs.myInput.focus(); } }">
  <input x-ref="myInput" type="text" placeholder="Click button to focus me">
  <button x-on:click="focusInput()">Focus Input</button>
  
  <!-- Access ref in expressions -->
  <p x-text="$refs.myInput?.value || 'Empty'"></p>
</div>

Multiple Refs & Shared Access

Multiple elements can share the same ref name—$refs.name returns an array when multiple elements match.

Refs are also accessible from child components, bubbling up the component tree until found.

Programmatic Refs

Use $ref(name) to get a ref, or $ref(name, value) to register any value (not just elements) as a ref.

Attribute Directives

Dynamic attribute binding for creating responsive and interactive elements.

x-bind:attr x:attr :attr Dynamic Attributes

Dynamically bind any HTML attribute. Supports multiple shorthand syntaxes for convenience.

<div x-data="{ disabled: false, url: 'https://fynejs.com' }">
  <!-- Full syntax -->
  <button x-bind:disabled="disabled">Button</button>
  
  <!-- x: shorthand -->
  <a x:href="url">Visit FyneJS</a>
  
  <!-- : shorthand (shortest!) -->
  <img :src="`/images/${imageFile}`" :alt="description">
  <input :placeholder="dynamicPlaceholder" :value="inputValue">
</div>

Syntax Options

x-bind:attr="expr" - Full syntax

x:attr="expr" - Short syntax

:attr="expr" - Shortest syntax (Vue-style)

x-class Dynamic Classes

Conditionally apply CSS classes. Supports string, array, and object syntax.

<div x-data="{ active: true, type: 'primary' }">
  <!-- Object syntax -->
  <button x-class="{ 'bg-blue-500': active, 'bg-gray-500': !active }">
    Toggle Button
  </button>
  
  <!-- String syntax -->
  <div x-class="`btn btn-${type}`"></div>
</div>

x-style Dynamic Styles

Apply dynamic inline styles. Supports both object and string syntax.

<div x-data="{ width: 50, color: 'red' }">
  <!-- Object syntax -->
  <div x-style="{ width: width + '%', backgroundColor: color }">
    Dynamic Box
  </div>
  
  <!-- String syntax -->
  <div x-style="`color: ${color}; font-size: 16px`"></div>
</div>

Event Directives

Handle user interactions with powerful event binding and modifiers.

x-on:event Event Listeners

Attach event listeners to elements using the x-on directive or the convenient @ shorthand.

<div x-data="{ count: 0 }">
  <!-- Long form syntax -->
  <button x-on:click="count++">Increment</button>
  <button x-on:click="count--">Decrement</button>
  
  <!-- Shorthand @ syntax -->
  <button @click="count++">Increment</button>
  <button @click="count--">Decrement</button>
  
  <!-- Form events -->
  <input @input="console.log($event.target.value)">
  <form @submit.prevent="handleSubmit()"></form>
  
  <!-- Keyboard events -->
  <input @keydown.enter="search()">
</div>

@ Shorthand Syntax

You can use @event as a shorthand for x-on:event. Both syntaxes work identically and support all the same modifiers.

Learn more: Event Callback Contexts for details on $event, $target variables and function arguments.

Event Modifiers

Powerful modifiers to control event behavior without writing additional JavaScript.

Execution Control

Control how events are handled

.prevent Calls preventDefault() - stops default browser behavior
.stop Calls stopPropagation() - prevents event bubbling
.self Only trigger when event target is the element itself
.once Remove event listener after first execution
.passive Add passive event listener for better performance
.capture Use capture phase instead of bubbling phase
.defer Execute handler in next microtask

Key Modifiers

Filter events by specific keys

.enter Enter key
.esc Escape key
.space Space key
.tab Tab key
.up Arrow Up
.down Arrow Down
.left Arrow Left
.right Arrow Right
.home Home key
.end End key
.delete Delete key
.backspace Backspace

Combination Keys

Require modifier keys to be held

.ctrl Require Ctrl key to be held down
.alt Require Alt key to be held down
.shift Require Shift key to be held down
.meta Require Meta/Cmd key to be held down

💡 Combination Example:

x-on:keydown.ctrl.s.prevent="save()"

Triggers save() when Ctrl+S is pressed

Mouse & Touch

Mouse buttons and touch interactions

.left Left mouse button only
.middle Middle mouse button (scroll wheel)
.right Right mouse button only
.outside Click outside the element
.window Bind the event on the window object
.single Single touch point
.multi Multiple touch points

Modifier Examples

<div x-data="{ message: '' }">
  <!-- Prevent form submission -->
  <form x-on:submit.prevent="handleSubmit()"></form>
  
  <!-- Enter key to search -->
  <input x-on:keydown.enter="search()" placeholder="Press Enter to search">
  
  <!-- Ctrl+S to save -->
  <div x-on:keydown.ctrl.s.prevent="save()">Press Ctrl+S to save</div>
  
  <!-- Click outside to close -->
  <div x-on:click.outside="isOpen = false">Modal content</div>
  
  <!-- Stop event bubbling -->
  <button x-on:click.stop="doNotBubble()">Isolated Click</button>
</div>

Component Directives

Advanced directives for building reusable components and managing component communication.

x-prop Reactive Props

Pass reactive properties to registered components, enabling parent-child component communication.

<!-- Parent component passing props -->
<div x-data="{ userName: 'John Doe', userRole: 'admin' }">
  <component source="user-card" x-prop="{ name: userName, role: userRole }"></component>
</div>

XTool.registerComponent Reusable Components

Register reusable components using the XTool.registerComponent() method with HTML template literals.

// Component registration
XTool.registerComponent({
    name: 'todo-item',
    template: html`
        <div x-data="{ completed: false }" class="todo-item">
            <input x-model="completed" type="checkbox">
            <span x-class="{ 'line-through': completed }" x-text="title"></span>
        </div>
    `
});

// Component usage in HTML
<div x-data="{ todos: ['Learn FyneJS', 'Build app'] }">
    <div x-for="todo in todos">
        <component source="todo-item" x-prop="{ title: todo }"></component>
    </div>
</div>

What's Next?