Skip to content

⚠️ Recent Visits is in beta. All listings and data shown are for demonstration purposes only and do not represent real or verified information. Tour listings are based on content from partner agencies’ websites and have not yet been confirmed by the agencies.

Recent Visits

SDK examples

Copy-and-paste integration examples for the Recent Visits widget: minimal, agency configuration, themed, Next.js, React with the package, and a headless quote request.

On this page

Examples marked check on this site are compiled or validated automatically against the SDK's real types whenever the documentation is tested, so they stay in step with the code. Replace rvk_AAAAAAAAAAAAAAAAAAAAAAAA with the key from your portal.

1. Minimal#

html
<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>My agency</title>
  </head>
  <body>
    <h1>Welcome</h1>
    <script
      src="https://recent-visit.com/widget/1/widget.js"
      data-key="rvk_AAAAAAAAAAAAAAAAAAAAAAAA"
      async
    ></script>
  </body>
</html>

2. Agency-specific configuration#

Everything comes from your key and your Widget Studio settings; you only add what you want to override. Here the page sets the launcher text and the assistant's name:

html
<script
  src="https://recent-visit.com/widget/1/widget.js"
  data-key="rvk_AAAAAAAAAAAAAAAAAAAAAAAA"
  data-config='{"launcher":{"label":"Ask Alpha Treks"},"assistant":{"name":"Trek Buddy"},"welcomeMessage":"Namaste! Ask me about our treks and tours."}'
  async
></script>

3. Theme customisation#

html
<script
  src="https://recent-visit.com/widget/1/widget.js"
  data-key="rvk_AAAAAAAAAAAAAAAAAAAAAAAA"
  data-config='{"appearance":"auto","theme":{"primary":"#7c2d12","accent":"#b45309","radius":20,"fontFamily":"\"Inter\", system-ui, sans-serif"},"themeDark":{"background":"#14110f"},"position":"left","offset":{"x":24,"y":24}}'
  async
></script>

The same in code, which is also how you change it while the page is running:

ts
import { init } from '@recent-visits/widget'

const widget = init({ key: 'rvk_AAAAAAAAAAAAAAAAAAAAAAAA' })
widget.update({ theme: { primary: '#7c2d12', radius: 20 }, position: 'left' })

4. Next.js (script tag, works today)#

Load the script once in your root layout:

tsx
import Script from 'next/script'
import type { ReactNode } from 'react'

export default function RootLayout({ children }: { children: ReactNode }) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://recent-visit.com/widget/1/widget.js"
          data-key={process.env.NEXT_PUBLIC_RECENT_VISITS_KEY}
          strategy="afterInteractive"
        />
      </body>
    </html>
  )
}

5. React or Next.js with the package (once published)#

init needs the browser, so call it in an effect and clean up. It is safe under React strict mode (the effect runs twice in development; destroy makes the second init start fresh).

tsx
'use client'

import { useEffect } from 'react'
import { init } from '@recent-visits/widget'

export function RecentVisitsWidget({ tourSlug }: { tourSlug?: string }) {
  useEffect(() => {
    const widget = init({
      key: process.env.NEXT_PUBLIC_RECENT_VISITS_KEY ?? '',
      pageContext: tourSlug ? { tourSlug } : undefined,
    })
    return () => widget.destroy()
  }, [tourSlug])
  return null
}

6. A tour page with context#

On a page about one of your tours, tell the widget which tour so suggestions fit it (the server accepts only your own published tours):

ts
import { init } from '@recent-visits/widget'

const widget = init({ key: 'rvk_AAAAAAAAAAAAAAAAAAAAAAAA' })
widget.update({
  pageContext: { tourSlug: 'poon-hill-5-days', title: 'Poon Hill trek, 5 days' },
  notifications: {
    enabled: true,
    invitations: [{ text: 'Questions about this trek?', tourPage: true }],
  },
})

7. Headless quote request#

Build your own form and send the request through the client. Only send consent: true after the visitor has actually agreed to share their details with your agency. The request is created for your agency and arrives in Leads; the traveller's email and phone stay hidden from you until they accept a quote.

ts
import { createClient, WidgetApiError } from '@recent-visits/widget'

const client = createClient({ key: 'rvk_AAAAAAAAAAAAAAAAAAAAAAAA' })

export async function sendQuote(form: {
  name: string
  email: string
  message: string
  agreed: boolean
}) {
  if (!form.agreed) return { ok: false as const, reason: 'Please agree to share your details.' }
  try {
    await client.quote({
      name: form.name,
      email: form.email,
      message: form.message,
      consent: true,
      tourSlug: 'poon-hill-5-days',
    })
    return { ok: true as const }
  } catch (error) {
    if (error instanceof WidgetApiError && error.code === 'rate_limited')
      return { ok: false as const, reason: 'Too many requests. Please try again later.' }
    return { ok: false as const, reason: 'Could not send your request. Please try again.' }
  }
}

Demo files#

The SDK repository includes runnable demo pages in apps/recent-visit-sdk/examples/ (index.html, themed.html, branding.html, server-branding.html). Serve the folder with any static server on a port whose domain you registered for Test (for example localhost) and put your Test key in the page.

Last checked against the product:

  • SDK installation · Install the Recent Visits widget with a script tag, add Subresource Integrity, run it locally, and understand the current npm status.
  • SDK configuration reference · Every option the Recent Visits widget accepts: type, default, limits and an example. Invalid values are replaced by safe defaults.
  • SDK API reference · The public surface of the Recent Visits widget package: init, the widget handle, the headless client, errors, types and the HTTP endpoints they use.

Still stuck? Contact us and tell us what you tried.