Embedding the Widget
The Levy Bookings widget is a tiny dependency-free JavaScript bundle (around 6 KB uncompressed) that loads from widget.levyelectric.com/v1.js and injects a secure iframe pointing to your hosted booking page. You can drop it onto any site that lets you paste an HTML snippet.
It works on:
- Squarespace
- Wix
- Webflow
- Shopify
- WordPress (any theme that supports a Custom HTML block)
- Hand-coded HTML / Next.js / React / Astro / Hugo / Jekyll
The script snippet
The fastest way to get the snippet is from the dashboard:
- Open Dashboard > Bookings > Widgets > {your widget}.
- Click Get embed code.
- Pick the mode (inline, modal, or floating-button).
- Copy the snippet and paste it into your site.
The snippet looks like this:
<!-- Inline embed -->
<div id="levy-bookings"></div>
<script
src="https://widget.levyelectric.com/v1.js"
data-slug="front-beach-bikes"
data-mode="inline"
data-target="#levy-bookings"
data-theme="light"
async
></script>
Embed modes
The widget supports three rendering modes, controlled by the data-mode attribute.
Inline
The widget injects an iframe directly into a target element on your page. Use this when you have a dedicated "Book a bike" page and you want the booking flow to feel like a native section.
<div id="levy-bookings"></div>
<script
src="https://widget.levyelectric.com/v1.js"
data-slug="front-beach-bikes"
data-mode="inline"
data-target="#levy-bookings"
async
></script>
The iframe auto-resizes as the customer progresses through the steps — no fixed height needed. A ResizeObserver inside the iframe emits levy:resize messages and the host script adjusts.
Modal
The widget injects a hidden iframe and opens it as a centered overlay modal when the customer clicks any element with data-levy-book="open". Use this when you want a "Book now" button anywhere on your site.
<button data-levy-book="open">Book a bike</button>
<script
src="https://widget.levyelectric.com/v1.js"
data-slug="front-beach-bikes"
data-mode="modal"
async
></script>
You can have multiple data-levy-book="open" triggers — every one of them opens the same modal.
Floating button
The widget injects a fixed "Book now" button in the bottom-right corner of every page that includes the snippet. Use this when you want a persistent call-to-action across the whole site (homepage, blog posts, product pages).
<script
src="https://widget.levyelectric.com/v1.js"
data-slug="front-beach-bikes"
data-mode="floating-button"
data-button-label="Book a bike"
data-button-position="bottom-right"
async
></script>
Data attribute reference
All configuration is on the <script> tag itself, using data-* attributes. The host script reads them once at load.
| Attribute | Required | Default | Description |
|---|---|---|---|
src | yes | — | Must be https://widget.levyelectric.com/v1.js. Use v1 for a stable contract. |
data-slug | yes | — | Your widget slug, e.g. front-beach-bikes. Matches the hosted URL. |
data-mode | yes | inline | inline, modal, or floating-button. |
data-target | inline only | #levy-bookings | CSS selector for the host element. Must exist on the page when the script loads. |
data-theme | no | light | One of light, dark, minimal. Overrides the widget's default theme. |
data-button-label | floating-button only | Book now | Text on the floating button. |
data-button-position | floating-button only | bottom-right | bottom-right, bottom-left, top-right, top-left. |
data-locale | no | browser | Force en, es, fr, de, or pt. Otherwise inferred from the customer's browser. |
data-utm-source | no | — | UTM parameters passed into the booking. Useful for attribution. |
data-pixel-ga4 | no | — | Forward conversion events to a GA4 measurement ID. |
data-pixel-meta | no | — | Forward conversion events to a Meta Pixel ID. |
async | recommended | — | Add async so the script does not block your page render. |
Conversion events
The widget bridges conversion events back to the parent page through postMessage. You can listen for them directly to forward into any analytics tool:
<script>
window.addEventListener('message', (event) => {
if (event.origin !== 'https://widget.levyelectric.com') return;
if (event.data?.type === 'levy:event') {
const { eventName, payload } = event.data;
// eventName: 'booking_started' | 'model_selected' | 'payment_succeeded' | ...
console.log('Levy booking event:', eventName, payload);
}
});
</script>
If you set data-pixel-ga4 or data-pixel-meta, the host script forwards the events for you automatically.
Custom domains
You can serve the booking page at your own domain (book.frankebikes.com) instead of fleets.levyelectric.com/book/{slug}:
- In your DNS provider, add a CNAME from
book.frankebikes.comtocname.levyelectric.com. - Open a support ticket from the dashboard with the desired domain. We provision the TLS certificate and routing within one business day.
- Update your widget's Public URL in the dashboard. Existing embed snippets keep working — the CNAME also passes the iframe.
Squarespace specifics
Squarespace blocks <script> tags in standard text blocks. To embed:
- Edit any page.
- Add a Code block (not a text block).
- Paste the snippet, click Apply.
- Make sure Display Source is off so the snippet executes.
For the floating-button mode, paste the snippet into Settings > Advanced > Code Injection > Footer. It then runs on every page of your Squarespace site.
Wix specifics
- From the editor, click Add Elements (+) > Embed Code > Embed HTML.
- Paste the snippet.
- Resize the placeholder to roughly the dimensions you expect the iframe to take.
For floating-button mode, use Settings > Custom Code > Add Custom Code and paste into the footer.
WordPress specifics
In a Custom HTML block (Gutenberg) or a Text widget (Classic), paste the snippet directly. If you use a page builder (Elementor, Divi, Beaver Builder), use that builder's HTML widget.
For the floating-button mode, paste into your theme's footer (or use a plugin like Insert Headers and Footers) so the script loads on every page.
Shopify specifics
- Open Themes > Edit code.
- Open
layout/theme.liquid. - Paste the floating-button snippet just before
</body>. The button now appears on every storefront page.
For an inline embed on a specific page, add a Custom Liquid section to that page and paste the inline snippet.
Performance
The host script is around 6 KB uncompressed and loads with async. The iframe itself lazy-loads (loading="lazy") and does not begin fetching Stripe.js until the customer reaches the payment step. Total time-to-interactive on a 3G simulation is under 200 ms.
If your site is privacy-restricted (Brave Shields, uBlock Origin, Firefox Total Cookie Protection), the iframe still works because we route everything through widget.levyelectric.com (not a tracking domain) and use first-party cookies inside the iframe.
Allowed origins
Before the widget will accept POST requests from your site, you must add the exact origin to the widget's Allowed origins list:
https://www.frankebikes.com
https://frankebikes.com
If your domain redirects from non-www to www (or vice versa), add both. If you preview on a staging URL, add that too. Origins are matched exactly (scheme + host + port). The hosted page (/book/{slug}) is always allowed and does not need an origin entry.
GET requests (availability, quote, config) are CORS-open to any origin — only POST requests (create booking, submit waiver) need to be on the allowlist.
Apple Pay
Apple Pay inside iframes is supported because we host the iframe on widget.levyelectric.com, which is registered as a verified merchant domain for every subaccount's Stripe connected account. No additional setup needed.