One page, whole journey
There are two ways to embed DriveCars, and they ship the same bundles, the same components and the same money rules. Pick one:
| Four elements | One element | |
|---|---|---|
| Pages | Four — a listing, a vehicle page, a basket, a checkout | One |
| Tags | <drivecars-search>, <drivecars-car>, <drivecars-cart>, <drivecars-checkout> | <drivecars-journey> |
| Navigation | Between your own URLs | None — the element swaps steps in place |
| Payment | On your checkout page | A step inside the element — the card form, then the confirmation, swapped in place like every other step |
| Used by | drivecars.ai itself | — |
Neither is a downgrade. The four-element integration fits a site that already has routes and wants the widget inside them; the single element fits a site that wants a booking flow dropped into one page.
The whole thing
<drivecars-journey
environment="sandbox"
country="ma"
city="casablanca"
></drivecars-journey>
<script src="https://sdk.drivecars.ai/v1/loader.js"></script>
<script src="https://sdk.drivecars.ai/v1/sdk.js"></script>
<script>
// Your backend mints this. The client_secret never reaches the browser.
const session = await fetch('/your/session-route', { method: 'POST' }).then((r) => r.json());
DriveCars.init({ session, environment: 'sandbox' });
DriveCars.mount('journey', document.querySelector('drivecars-journey'));
</script>That is the integration. There is no second page to build and no event to listen for — the element owns every hop.
How it moves
- Search. The customer searches and sees results.
- Vehicle detail, in place. Clicking a result swaps the step; the URL does not change. The dates and hours they already chose carry across, so nothing is asked twice.
- Back returns to the results with filters and scroll intact — the search step stays mounted, so nothing is refetched.
- Basket, once a car is added.
- Payment, in a modal, when they continue.
Attributes
The union of what the composed elements take. Every one is optional except a publishable key if you want payment to work.
| Attribute | |
|---|---|
environment | production (default) or sandbox. Decides the API host. |
base-url | Overrides environment. For local development. |
token, customer-token | Set by DriveCars.mount(); you rarely write these. |
country, city | Seed the search. |
date-from, date-to | YYYY-MM-DD. Seed the dates. |
pickup-hour, return-hour | 0–23. |
driver-age, car-group | Seed the remaining search fields. |
lang, display-currency | As elsewhere. |
maps-key | Map tiles on the results step. |
Events
| Event | |
|---|---|
bookingCreated | A draft booking exists; detail.reference is its reference. Useful for your own analytics. |
dc:analytics | Every analytics event the composed elements emit. |
tokenRefreshNeeded | The session needs refreshing, as elsewhere. |
You do not need to listen for anything to make the journey work. That is the point of this integration.
One thing worth knowing about the payment step
The card form cannot mount inside a shadow root. On the payment step it therefore renders into the light DOM — two nodes placed immediately after the element, the card form and then the pay button — rather than inside the element’s own tree. They carry their own inline styles for the same reason: your stylesheet applies to them, and the widget’s compiled one does not.
If you style .dc-journey-payment-modal, style it as part of your own page.
Can I use both?
On the same page, no — mount either <drivecars-journey> or the four
elements. They would otherwise compete for the same cart.
Across a site, yes: nothing stops a single-element booking widget on a landing page and the four-element integration in a fuller flow elsewhere.