ResytechResytech Docs

Framework Integration

Using Resytech web components with React, Vue, Svelte, Angular, and other frameworks.

Resytech web components are standard Custom Elements. They work in any framework that renders HTML, but each framework has slightly different patterns for setting properties and listening to events.

Vue

Vue has full web component support out of the box. Use v-bind for properties and v-on for events.

<template>
  <resytech-activity-list @activity-select="onActivitySelect" />

  <resytech-equipment-list
    :activity-id="selectedActivity?.uuid"
    @equipment-select="onEquipmentSelect"
  />

  <resytech-calendar
    ref="calendar"
    :show-prices="true"
    @date-select="onDateSelect"
  />
</template>

<script setup>
import { ref, onMounted } from 'vue';

const selectedActivity = ref(null);
const calendar = ref(null);

onMounted(async () => {
  const api = new ResytechApi();
  await api.initialization.initialize({ identifier: 'my-site' });
  api.registerComponents();
});

function onActivitySelect(e) {
  selectedActivity.value = e.detail.activity;
}

function onEquipmentSelect(e) {
  // Set object properties via ref for complex data
  calendar.value.activityId = selectedActivity.value.uuid;
  calendar.value.load();
}

function onDateSelect(e) {
  console.log('Selected:', e.detail.date);
}
</script>

Vue Compiler Hint

Vue may warn about unknown custom elements. Tell the compiler to treat resytech-* tags as custom elements:

// vite.config.js
import vue from '@vitejs/plugin-vue';

export default {
  plugins: [
    vue({
      template: {
        compilerOptions: {
          isCustomElement: (tag) => tag.startsWith('resytech-')
        }
      }
    })
  ]
};

Svelte

Svelte handles web components natively. Use on: for events and bind:this for property access.

<script>
  import { onMount } from 'svelte';

  let calendarEl;
  let selectedActivity = null;

  onMount(async () => {
    const api = new ResytechApi();
    await api.initialization.initialize({ identifier: 'my-site' });
    api.registerComponents();
  });

  function onActivitySelect(e) {
    selectedActivity = e.detail.activity;
  }

  function onEquipmentSelect(e) {
    calendarEl.activityId = selectedActivity.uuid;
    calendarEl.load();
  }

  function onDateSelect(e) {
    console.log('Selected:', e.detail.date);
  }
</script>

<resytech-activity-list on:activity-select={onActivitySelect} />

<resytech-equipment-list
  activity-id={selectedActivity?.uuid}
  on:equipment-select={onEquipmentSelect}
/>

<resytech-calendar
  bind:this={calendarEl}
  show-prices="true"
  on:date-select={onDateSelect}
/>

No additional configuration needed.


React 19+

React 19 added native support for custom elements. Properties, events, and refs all work as expected.

import { useRef, useEffect, useState } from 'react';

function BookingPage() {
  const calendarRef = useRef(null);
  const [selectedActivity, setSelectedActivity] = useState(null);

  useEffect(() => {
    const api = new ResytechApi();
    api.initialization.initialize({ identifier: 'my-site' }).then(() => {
      api.registerComponents();
    });
  }, []);

  return (
    <>
      <resytech-activity-list
        onActivitySelect={(e) => setSelectedActivity(e.detail.activity)}
      />

      <resytech-equipment-list
        activity-id={selectedActivity?.uuid}
        onEquipmentSelect={(e) => {
          calendarRef.current.activityId = selectedActivity.uuid;
          calendarRef.current.load();
        }}
      />

      <resytech-calendar
        ref={calendarRef}
        show-prices="true"
        onDateSelect={(e) => console.log('Selected:', e.detail.date)}
      />
    </>
  );
}

React 19 maps onEventName props to custom element event listeners automatically. The event name is derived by lowercasing: onActivitySelect listens for activityselect. Since our events use kebab-case (activity-select), you may need to use the ref approach below for event listeners.

For reliable event handling with kebab-case event names, use refs:

import { useRef, useEffect, useState } from 'react';

function BookingPage() {
  const listRef = useRef(null);
  const [selectedActivity, setSelectedActivity] = useState(null);

  useEffect(() => {
    const el = listRef.current;
    const handler = (e) => setSelectedActivity(e.detail.activity);
    el.addEventListener('activity-select', handler);
    return () => el.removeEventListener('activity-select', handler);
  }, []);

  return <resytech-activity-list ref={listRef} />;
}

React 18 and Earlier

React 18 does not natively support custom element properties or events in JSX. Use refs for everything.

import { useRef, useEffect, useState } from 'react';

function ActivityList() {
  const ref = useRef(null);
  const [selected, setSelected] = useState(null);

  useEffect(() => {
    const el = ref.current;

    // Listen for custom events via addEventListener
    const handler = (e) => setSelected(e.detail.activity);
    el.addEventListener('activity-select', handler);
    return () => el.removeEventListener('activity-select', handler);
  }, []);

  return <resytech-activity-list ref={ref} />;
}

function EquipmentList({ activity }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current && activity) {
      // Set complex object properties via ref (JSX only passes strings)
      ref.current.activity = activity;
    }
  }, [activity]);

  return <resytech-equipment-list ref={ref} />;
}

function Calendar({ activityId, durationId }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current && activityId) {
      ref.current.activityId = activityId;
      ref.current.durationId = durationId;
      ref.current.load();
    }
  }, [activityId, durationId]);

  useEffect(() => {
    const el = ref.current;
    const handler = (e) => console.log('Date:', e.detail.date);
    el.addEventListener('date-select', handler);
    return () => el.removeEventListener('date-select', handler);
  }, []);

  return <resytech-calendar ref={ref} show-prices="true" />;
}

TypeScript Declarations for React

If TypeScript complains about unknown JSX elements, add type declarations:

// resytech.d.ts
declare namespace JSX {
  interface IntrinsicElements {
    'resytech-activity-card': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'activity-id'?: string;
      'show-tagline'?: string;
      'show-description'?: string;
      'show-price'?: string;
    };
    'resytech-activity-list': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'show-title'?: string;
      'title-text'?: string;
    };
    'resytech-equipment-list': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'activity-id'?: string;
      'show-title'?: string;
    };
    'resytech-calendar': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'activity-id'?: string;
      'duration-id'?: string;
      'show-prices'?: string;
    };
    'resytech-timeslots': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'activity-id'?: string;
      date?: string;
      'duration-id'?: string;
    };
    'resytech-addon-selector': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'activity-id'?: string;
      date?: string;
      time?: string;
    };
    'resytech-cart-summary': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
    'resytech-duration-selector': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;
    'resytech-booking-embed': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      'base-url'?: string;
      'location-id'?: string;
      'activity-id'?: string;
      height?: string;
    };
  }
}

Angular

Angular supports web components via the CUSTOM_ELEMENTS_SCHEMA:

// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core';

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
<!-- app.component.html -->
<resytech-activity-list
  (activity-select)="onActivitySelect($event)">
</resytech-activity-list>

<resytech-calendar
  #calendar
  [attr.activity-id]="selectedActivity?.uuid"
  [attr.show-prices]="'true'"
  (date-select)="onDateSelect($event)">
</resytech-calendar>
// app.component.ts
@ViewChild('calendar') calendarRef: ElementRef;

onActivitySelect(event: CustomEvent) {
  this.selectedActivity = event.detail.activity;
  this.calendarRef.nativeElement.activityId = this.selectedActivity.uuid;
  this.calendarRef.nativeElement.load();
}

Vanilla JavaScript

No framework needed. The components work directly in HTML:

<script src="https://js.resytech.com/1/resytech.js"></script>
<script>
  const api = new ResytechApi();
  api.initialization.initialize({ identifier: 'my-site' }).then(() => {
    api.registerComponents();
  });

  document.addEventListener('activity-select', (e) => {
    console.log(e.detail.activity.name);
  });
</script>

<resytech-activity-list></resytech-activity-list>

Summary

FrameworkAttributesObject PropertiesCustom EventsExtra Config
Vanilla JSNativeNativeNativeNone
VueNativev-bind / refv-onisCustomElement compiler option
SvelteNativebind:thison:eventNone
React 19+Nativerefref + addEventListenerNone
React 18Nativeref onlyref + addEventListenerTypeScript declarations
Angular[attr.x]ViewChild + .nativeElement(event)CUSTOM_ELEMENTS_SCHEMA

On this page