| Install | |
|---|---|
composer require laraextend/scroll-reveal |
|
| Latest Version: | v0.0.1 |
| PHP: | ^8.2 |
Scroll-triggered animations for Laravel Blade — powered by Animate.css.
laraextend/scroll-reveal provides a flexible <x-scroll-reveal> Blade component that wraps any content in a scroll-triggered animation. It ships with its own zero-dependency JavaScript driver (built on the native Intersection Observer API) and works seamlessly in plain Blade templates and Livewire.
No external JavaScript runtime required. The package includes its own driver script (
scroll-reveal-driver.js) — no third-party npm package needed.
<x-scroll-reveal> wraps any HTML content with scroll-triggered animations<x-scroll-reveal-scripts> injects a zero-dependency Intersection Observer driver — no npm package requiredfade-up, zoom-in, slide-left, …) mapped to Animate.css classeswire:*, x-* and data-* attributes are forwarded automatically; re-initialized after livewire:navigateddiv, section, article, span, …) via the as propduration and delay props per element:animate="false" to disable animation for a specific element at runtimecomposer require laraextend/scroll-reveal
The ServiceProvider is registered automatically via Laravel's Auto-Discovery.
This package generates the HTML data-* attributes for the animation driver. You have three options for the frontend setup — choose whichever fits your project.
The driver is bundled via Vite — no <x-scroll-reveal-scripts> in the layout required.
Step 1 — Publish the driver to resources/js/:
php artisan vendor:publish --tag=scroll-reveal-js
This copies scroll-reveal-driver.js to resources/js/scroll-reveal-driver.js so Vite can bundle it.
Step 2 — Install Animate.css:
npm install animate.css
Step 3 — Add to resources/js/app.js:
import './scroll-reveal-driver.js'; // sets window.ScrollRevealDriver
import 'animate.css';
let driver;
function init() {
if (driver) driver.destroy();
driver = new ScrollRevealDriver({ initClass: 'animateme', offset: 0.2 });
}
document.addEventListener('DOMContentLoaded', init);
document.addEventListener('livewire:navigated', init);
Step 4 — Import the CSS in your stylesheet (e.g. resources/css/app.css):
@import 'animate.css';
Step 5 — Restart the dev server:
npm run dev
The package ships its own Intersection Observer driver. Publish it to your public/ directory once:
php artisan vendor:publish --tag=scroll-reveal-assets
This copies scroll-reveal-driver.js to public/vendor/scroll-reveal/scroll-reveal-driver.js.
Then add <x-scroll-reveal-scripts> and the Animate.css link to your layout (e.g. resources/views/layouts/app.blade.php):
Inside <head>:
<!-- Animate.css -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css/animate.min.css" />
Before the closing </body> tag:
{{-- Loads the published driver + auto-initializes + handles livewire:navigated --}}
<x-scroll-reveal-scripts />
That is all. No npm install. No JS configuration. The component handles initialization and Livewire SPA navigation automatically.
Available props for <x-scroll-reveal-scripts>:
| Prop | Type | Default | Description |
|---|---|---|---|
initClass |
string |
'animateme' |
CSS class the driver watches. Must match what the component sets. |
offset |
float |
0.2 |
Fraction of the element that must be visible to trigger (0–1). |
animateOut |
bool |
false |
Re-animate when an element leaves and re-enters the viewport. |
inline |
bool |
false |
Embed the driver script inline instead of referencing the file (useful if you skip the publish step). |
Inline mode (no publish step needed at all):
<x-scroll-reveal-scripts :inline="true" />
Inside <head>:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/animate.css/animate.min.css" />
Before the closing </body> tag:
<x-scroll-reveal-scripts />
No config files, no migrations, no additional steps required.
Publish the config file to set project-wide defaults:
php artisan vendor:publish --tag=scroll-reveal-config
Published file: config/scroll-reveal.php
<x-scroll-reveal>
<p>This fades in from below when it scrolls into view.</p>
</x-scroll-reveal>
<x-scroll-reveal animation="zoom-in" :duration="600" :delay="150">
<div class="card">...</div>
</x-scroll-reveal>
<x-scroll-reveal
animate="true"
animation="fade-up"
:duration="550"
:delay="200"
class="container mx-auto px-4"
>
<h2 class="text-3xl font-bold">Welcome</h2>
<p class="mt-2 text-gray-600">Animated section content.</p>
</x-scroll-reveal>
{{-- Renders as <section> --}}
<x-scroll-reveal as="section" animation="slide-up" class="py-16 bg-gray-50">
...
</x-scroll-reveal>
{{-- Renders as <article> --}}
<x-scroll-reveal as="article" animation="fade-right">
...
</x-scroll-reveal>
<x-scroll-reveal :animate="false" class="container">
{{-- No animation applied, just renders a plain <div> --}}
...
</x-scroll-reveal>
All standard Blade and Livewire attributes pass through without extra configuration:
<x-scroll-reveal
wire:key="feature-{{ $feature->id }}"
animation="fade-up"
:delay="$loop->index * 100"
class="feature-card"
>
<livewire:feature-card :feature="$feature" />
</x-scroll-reveal>
<x-scroll-reveal> Props Reference| Prop | Type | Default | Description |
|---|---|---|---|
animate |
bool |
true |
Enable or disable the animation entirely. |
animation |
string |
'fade-up' |
Animation alias (see Animation Aliases). |
duration |
int |
700 |
Animation duration in milliseconds. |
delay |
int |
0 |
Animation delay in milliseconds (0 = no delay). |
as |
string |
'div' |
HTML tag to render (div, section, article, span, …). |
All additional attributes (e.g. class, id, wire:*, x-*, data-*) are forwarded directly to the rendered element.
All aliases are mapped to their corresponding Animate.css class names.
| Alias | Animate.css class |
|---|---|
fade |
fadeIn |
fade-up |
fadeInUp |
fade-down |
fadeInDown |
fade-left |
fadeInRight |
fade-right |
fadeInLeft |
| Alias | Animate.css class |
|---|---|
zoom-in |
zoomIn |
zoom-out |
zoomOut |
zoom-up |
zoomInUp |
zoom-down |
zoomInDown |
zoom-left |
zoomInRight |
zoom-right |
zoomInLeft |
| Alias | Animate.css class |
|---|---|
slide-up |
slideInUp |
slide-down |
slideInDown |
slide-left |
slideInRight |
slide-right |
slideInLeft |
| Alias | Animate.css class |
|---|---|
flip-up |
flipInX |
flip-down |
flipInX |
flip-left |
flipInY |
flip-right |
flipInY |
| Alias | Animate.css class |
|---|---|
rotate-in |
rotateIn |
rotate-left |
rotateInUpLeft |
rotate-right |
rotateInUpRight |
| Alias | Animate.css class |
|---|---|
swing-in |
jackInTheBox |
drop |
bounceInDown |
rise |
bounceInUp |
skew-left |
lightSpeedInRight |
skew-right |
lightSpeedInLeft |
blur-in |
zoomIn |
Unknown aliases fall back to
fadeInUp.
After publishing the config file, you can set project-wide defaults in config/scroll-reveal.php:
return [
'animate' => true,
'animation' => 'fade-up',
'duration' => 700,
'delay' => 0,
'as' => 'div',
// Informational — mirrors the props of <x-scroll-reveal-scripts>
'driver_options' => [
'initClass' => 'animateme',
'offset' => 0.2,
'animateOut' => false,
],
];
Note: The component props override the config defaults. The
driver_optionskey is informational only — it is not automatically injected into JavaScript.
The <x-scroll-reveal> Blade component renders a standard HTML element.
When animate is true, it adds the CSS class animateme and three data-* attributes:
<div
class="animateme your-classes"
data-sr-anim="fadeInUp"
data-sr-duration="550ms"
data-sr-delay="0.2s"
>
...
</div>
The JavaScript driver observes all .animateme elements via the Intersection Observer API.
When an element enters the viewport, the driver reads the data-sr-* attributes and applies the corresponding animate__ CSS class from Animate.css.
When animate is false, no attributes are added — a plain element is rendered.
composer install
./vendor/bin/pest
MIT — see LICENSE.