Laravel
Keys in config, one Blade partial in your layout. Using Inertia with React or Vue? Follow that framework instead.
Installed with script tags in your templates · Written for Laravel 11 and 12, Blade
What works
| Product | Status | Note |
|---|---|---|
| Analytics & custom events | Supported | |
| Feedback widget | Supported | |
| Review badge | Supported | |
| Web push | Supported | public/ is served at the site root |
| Roadmap embed | Supported | |
| Feedback, survey & roadmap links | Supported |
1. Add your keys
Only the keys for products you use - the host is always needed.
.env
USERLILY_HOST=https://dev.userlily.com USERLILY_TRACKING_ID=YOUR_TRACKING_ID USERLILY_CAMPAIGN=YOUR_CAMPAIGN_KEY USERLILY_BROADCAST_KEY=YOUR_PROJECT_KEY
2. Read them in config
Through config/services.php, not env() in a template: once config is cached, env() returns null outside config files.
config/services.php - add to the returned array
'userlily' => [
'host' => env('USERLILY_HOST'),
'tracking_id' => env('USERLILY_TRACKING_ID'),
'campaign' => env('USERLILY_CAMPAIGN'),
'broadcast_key' => env('USERLILY_BROADCAST_KEY'),
],3. Create the partial
Each product loads only when its key is set.
resources/views/partials/userlily.blade.php
@php($ul = config('services.userlily'))
@if ($ul['tracking_id'])
<script src="{{ $ul['host'] }}/v1/tracker.js"
data-tracking-id="{{ $ul['tracking_id'] }}" async></script>
@endif
@if ($ul['campaign'])
<script src="{{ $ul['host'] }}/v1/widget.js"
data-campaign-id="{{ $ul['campaign'] }}" async></script>
@endif
@if ($ul['broadcast_key'])
<script src="{{ $ul['host'] }}/v1/broadcast.js"
data-project="{{ $ul['broadcast_key'] }}" defer></script>
@endif4. Include it in your layout
Just before </body>. Component layouts ({{ $slot }}) take the same include.
resources/views/layouts/app.blade.php
<body>
@yield('content')
@include('partials.userlily')
</body>5. Send custom events
From any page. window.userlily exists once the tracker has loaded - well before anyone can submit a form.
In a template
<script>
document
.querySelector('#signup-form')
.addEventListener('submit', function () {
window.userlily.track('signup_completed')
})
</script>6. Show the review badge
The badge renders exactly where the tag sits. Switch on “Enable syndication” on the campaign’s Syndication page first.
In a template
{{-- Where the badge should appear --}}
<script
src="{{ config('services.userlily.host') }}/v1/syndication.js"
data-token="YOUR_CAMPAIGN_KEY" async></script>7. Host the push worker
The worker must be served from your own domain at /userlily-sw.js, as JavaScript, with no redirect and over HTTPS. Each of these fails silently. Laravel serves public/ at the site root, so download it there.
Terminal, in your project root
curl -o public/userlily-sw.js https://dev.userlily.com/v1/userlily-sw.js
8. Ask for push permission from your own button
Optional. Works when the project’s “When to ask” is set to “Only when your own code asks”.
In a template
<button id="notify-me">Get notified</button>
<script>
var button = document.querySelector('#notify-me')
button.addEventListener('click', function () {
// Exists once broadcast.js has started, and only
// when "When to ask" is "Only when your own code asks".
if (window.userlilyBroadcast) window.userlilyBroadcast.prompt()
})
</script>9. Embed the public roadmap
Turn on “Enable public roadmap” in Roadmap → Settings first. An Allowed domain on the roadmap limits which sites can embed it.
Where the roadmap should appear
<iframe
data-userlily-roadmap
src="https://dev.userlily.com/r/YOUR_ROADMAP_KEY?embed=1"
title="Product roadmap"
style="width:100%;height:720px;border:0"
loading="lazy"
></iframe>
<script>
(function () {
window.addEventListener("message", function (event) {
if (event.origin !== "https://dev.userlily.com") return;
var data = event.data;
if (!data || data.type !== "userlily:roadmap:height") return;
var frame = document.querySelector("iframe[data-userlily-roadmap]");
if (frame && data.height > 0) frame.style.height = data.height + "px";
});
}());
</script>10. Share links
Every campaign, survey and public roadmap also has a hosted page - nothing to install. Find each one under Share in the dashboard.
Hosted pages
Feedback form https://dev.userlily.com/f/YOUR_CAMPAIGN_KEY Survey https://dev.userlily.com/s/YOUR_SURVEY_KEY Roadmap https://dev.userlily.com/r/YOUR_ROADMAP_KEY
Notes
- Inertia with React or Vue: follow the React or Vue guide instead. Laravel’s Vite exposes
VITE_-prefixed variables from.envto the browser.
Every option, limit and troubleshooting step is in the main docs. Logos are trademarks of their owners and are shown only to indicate compatibility.