July 30, 2026 — The unofficial Laracon US Day 3. Get your ticket to The Vibes

You're viewing pre-release documentation — version 4.x is in beta

Features, APIs and behaviour may change before the stable release. View the stable version (3.x)

Gestures & Animation


Overview#

Smooth, finger-tracking animation can't wait on a PHP round-trip — at 120fps there's no time to ask the server where a view should be. SuperNative solves this with shared values: a numeric value that lives on the native side and is mutated on the UI thread by gestures and animations. PHP holds only an opaque handle; the per-frame numbers never cross the boundary.

If you've used Reanimated in React Native, this is the same idea.

Shared values#

Create one in PHP with SharedValue::make($initial), bind it to a gesture, and read it from any animatable prop:

Copied!
@php $drag = \Native\Mobile\Edge\SharedValue::make(); @endphp
 
<native:gesture-area :pan-y="$drag" @drag-end="onRelease">
<native:column
:translate-y="$drag"
:opacity="$drag->interpolate([0, 200], [1, 0])"
:scale="$drag->interpolate([0, 200], [1, 0.7])"
class="p-6 bg-theme-surface rounded-2xl">
<native:text>Pull me down</native:text>
</native:column>
</native:gesture-area>

As the user drags, $drag updates on the UI thread and the bound props follow instantly — the card moves, fades, and shrinks together, with no PHP involved during the gesture.

Formulas#

Rather than bind the raw value everywhere, derive from it with a chainable formula. Each method returns a new derived value, so one gesture can drive many props differently:

  • ->interpolate([$inMin, $inMax], [$outMin, $outMax]) — linearly remap a range (clamps at the ends).
  • ->clamp($min, $max) — constrain to a range.
  • ->multiply($factor) — scale.
  • ->add($offset) — shift.
Copied!
$drag->interpolate([0, 200], [1, 0]); // 0→1, 200→0, fully faded past 200

Animatable props that accept a shared value: translate-x, translate-y, scale, rotate, and opacity.

Reacting when the gesture ends#

Per-frame values stay on the native side, but you often need PHP to make a decision when the user lets go — commit or snap back. The <native:gesture-area> fires @drag-end with the final value:

Copied!
public function onRelease(float $value): void
{
if ($value > 150) {
$this->dismiss(); // dragged far enough — commit
}
// otherwise the view springs back to its resting position
}

Call ->value() on a shared value inside such a handler to read its PHP-side snapshot (with any formula applied) if you need the derived number rather than the raw translation.

Property animations#

Any element animates when a value you bind to it changes between renders — no shared value, no gesture. Wrap the change in animate-duration (milliseconds) and the element eases from its old value to the new one on the next render. The animatable props are translate-x, translate-y, scale, rotate, and opacity:

Copied!
{{-- A panel slides up from below when $shown flips to true --}}
<native:column
:translate-y="$shown ? 0 : 120"
:animate-duration="450"
animate-easing="ease-out"
class="bg-theme-surface rounded-2xl p-4">
<native:text>Surprise!</native:text>
</native:column>

Toggle $shown in a method and the panel eases into place — there's no animation code to write beyond the props.

Duration & easing#

  • animate-duration — length in milliseconds (e.g. 250). Without it, a change snaps instantly.
  • animate-easing — the curve: linear, ease-in, ease-out, or ease-in-out.

Combine transforms freely under one duration — a card can slide, fade, and scale at once by animating translate-y, opacity, and scale together.

Looping#

Set animate-loop to repeat an animation continuously — a pulsing dot, a breathing highlight:

Copied!
<native:column :scale="$pulsing ? 1.15 : 1.0" :animate-duration="600" :animate-loop="true">
<native:icon name="heart.fill" />
</native:column>

Press feedback#

Press feedback is a special case: it reacts the instant a finger touches down — before any PHP round-trip — so a tap feels immediate. Set any of these on a pressable element and the effect holds while pressed, then springs back on release:

  • press-scale — scale while pressed (e.g. 0.9 to shrink slightly)
  • press-opacity — dim while pressed (e.g. 0.55)
  • press-translate-y — nudge down while pressed (e.g. 3)
Copied!
<native:pressable :press-scale="0.92" :press-opacity="0.85" :press-translate-y="3" @press="open">
<native:text>Press me</native:text>
</native:pressable>

Unlike animate-* (which runs when a re-render changes a value), press feedback needs no state and no handler — the native side plays it locally on touch.