React components
Put an Agoo booking widget or visitor pre-registration form in your React or Next.js site, themed to match it.
This is designed and scheduled but not built yet. We document it now so you can plan your integration.
@ardent-africa/agoo-react gives you two components that run in your own pages: a booking widget for one of your booking types, and a pre-registration form that lets visitors register before they arrive. They use your organisation's branding from Agoo by default, and you can restyle them with CSS variables.
They work with a publishable key (agoo_pk_…), which is safe in the browser: it can only read your public booking types and the visitor types open for pre-registration, and create bookings and pre-registrations. It can't read visits, people, full forms or anything else.
Not using React? Use the embed script or the WordPress plugin instead.
Plan access
The components call the Agoo API directly from the browser, so live use follows API plan access: Pro and Enterprise. Test keys work on every plan. On other plans, use the embed script, which shows your hosted booking and pre-registration pages in a frame and works on every plan that has booking pages.
The install command will work once the package is published to npm.
Install
npm i @ardent-africa/agoo-reactRequires React 18.3 or 19. Import the stylesheet once, for example in your root layout:
import "@ardent-africa/agoo-react/styles.css"Get a publishable key
An admin creates one in Console → Developers → API keys → Create key → Publishable. Use agoo_pk_test_… while you build: test bookings go to your test-mode sandbox and send no SMS, WhatsApp or email. Switch to agoo_pk_live_… when you go live.
Components
<AgooProvider>
Wrap the components in a provider once. It holds the key and loads your organisation's branding.
Prop
Type
<BookingWidget>
Shows available times for a booking type, collects the attendee's details and intake questions, and makes the booking. Agoo then sends the confirmation and reminders, and an in-person booking becomes an expected visit with a QR pass, exactly as from your own booking page. If the booking type requires the host's confirmation, the booking is a pending request until the host confirms it, and the widget tells the attendee so.
The intake questions come from the booking type's public_form in GET/booking-types: the questions asked of the visitor on its visit type's form, never the fields your staff fill in. Publishing a booking type makes these questions public, whether or not its visit type is open for pre-registration.
Prop
Type
<PreRegistrationForm>
Lets a visitor register before they arrive at a site to see a host you choose: who they are, and the custom questions your organisation asks for that visitor type. They get their QR pass by SMS, WhatsApp or email. If your organisation requires host approval, the host approves before the pass is sent.
The form fetches what it needs itself, with the publishable key, so there's nothing to configure in your code:
- It lists the visitor types open for pre-registration with GET/visit-types. A publishable key sees only types that are turned on and that an admin has opened for pre-registration in Console → Settings → Forms.
- It shows the chosen type's questions from its
public_form: only the fields asked of the visitor, in the order and with the labels and help text set in the form builder. Fields your staff fill in, approval rules and watchlist settings are never sent to the browser. - It checks the answers against that schema, then creates the pre-registration with POST/visits.
When an admin publishes a new version of the form, the component shows the new questions the next time it loads.
The host is set by your page
A publishable key can't list or search your people, so your staff directory can't be read from a web page. Set host to the person the visitor
is coming to see, for example on a page for one person or one team. Without it, the component shows a message saying the form needs a host, and
creates nothing. If visitors need to pick their host themselves, use the embed script: it
shows your hosted pre-registration page, which has its own host search.
Prop
Type
What the callbacks receive
Because a publishable key can't read your data, the callbacks get a short summary, not the full object:
type BookingSummary = {
booking_id: string // booking_…
status: "confirmed" | "pending" // pending: waiting for the host to confirm
start_at: string // RFC 3339, UTC
end_at: string
}
type PreRegistrationSummary = {
visit_id: string // visit_…
status: "expected" | "awaiting_approval"
expected_at: string
}To act on the full booking or visit on your server, listen for the booking.created or visit.created webhook rather than sending data from the browser.
Next.js App Router example
The components are interactive, so they must render in a Client Component. Keep the page itself a Server Component and import a small client wrapper:
"use client"
import { useRouter } from "next/navigation"
import { AgooProvider, BookingWidget } from "@ardent-africa/agoo-react"
export function Booking() {
const router = useRouter()
return (
<AgooProvider publishableKey={process.env.NEXT_PUBLIC_AGOO_PUBLISHABLE_KEY!}>
<BookingWidget bookingType="btype_01jv4dk79q9g8xe6szaeavsntc" onBooked={(booking) => router.push(`/book/thanks?ref=${booking.booking_id}`)} />
</AgooProvider>
)
}import { Booking } from "./booking"
export const metadata = { title: "Book a consultation" }
export default function Page() {
return (
<main>
<h1>Book a consultation</h1>
<Booking />
</main>
)
}import "@ardent-africa/agoo-react/styles.css"
import "./globals.css"
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}NEXT_PUBLIC_AGOO_PUBLISHABLE_KEY=agoo_pk_test_…Only the publishable key may have the NEXT_PUBLIC_ prefix. Never expose a secret key (agoo_sk_…) this way: anything prefixed NEXT_PUBLIC_ is sent to the browser.
Theming
By default the components use your organisation's branding from Agoo: the colours, logo and font you set in the console, with the same automatic contrast check. To fit them into your site's design, override these CSS variables on any parent element:
| Variable | Controls |
|---|---|
--agoo-color-primary | Buttons, selected slots, links |
--agoo-color-primary-foreground | Text on primary-coloured elements |
--agoo-color-background | Component background |
--agoo-color-foreground | Main text |
--agoo-color-muted | Secondary text and unavailable slots |
--agoo-color-border | Borders and dividers |
--agoo-color-danger | Error messages |
--agoo-radius | Corner radius of buttons, inputs and cards |
--agoo-font-family | Font for all text |
.booking-section {
--agoo-color-primary: #0b5d4b;
--agoo-color-primary-foreground: #ffffff;
--agoo-radius: 6px;
--agoo-font-family: inherit;
}
@media (prefers-color-scheme: dark) {
.booking-section {
--agoo-color-background: #111614;
--agoo-color-foreground: #f2f5f4;
--agoo-color-border: #2a332f;
}
}Variables you set win over your organisation's branding. If you choose colours yourself, check that text keeps a contrast ratio of at least 4.5:1 against its background.
Behaviour you get for free
- Accessible. Keyboard navigation, visible focus, labelled fields and announced errors, designed to meet WCAG 2.2 AA.
- Phone numbers. Ghana numbers can be typed as
024 123 4567and are stored as+233241234567. - Time zones. Slots show in the booking type's time zone, with the visitor's own time alongside when they differ.
- Privacy notice. The visitor sees your organisation's privacy notice before submitting, as on every Agoo page for visitors.
- Double booking. If someone else takes a slot first, the widget explains and shows the next free times.
Content Security Policy
If your site sends a Content-Security-Policy header, allow the components to call the Agoo API:
connect-src 'self' https://api.agoo.ardent.africa;
img-src 'self' https://app.agoo.ardent.africa;img-src is for your organisation's logo. The components don't load scripts or frames from other origins. The embed script needs a different policy.