# Framework Integration (/developers/web-components-frameworks)



Resytech web components are standard [Custom Elements](https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_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]

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

```vue
<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-compiler-hint]

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

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

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

***

Svelte [#svelte]

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

```svelte
<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]

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

```jsx
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)}
      />
    </>
  );
}
```

<Callout type="info">
  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.
</Callout>

React 19 — Ref Approach (Recommended) [#react-19--ref-approach-recommended]

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

```jsx
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-and-earlier]

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

```jsx
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 [#typescript-declarations-for-react]

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

```typescript
// 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]

Angular supports web components via the `CUSTOM_ELEMENTS_SCHEMA`:

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

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule {}
```

```html
<!-- 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>
```

```typescript
// 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 [#vanilla-javascript]

No framework needed. The components work directly in HTML:

```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 [#summary]

| Framework      | Attributes | Object Properties              | Custom Events              | Extra Config                      |
| -------------- | ---------- | ------------------------------ | -------------------------- | --------------------------------- |
| **Vanilla JS** | Native     | Native                         | Native                     | None                              |
| **Vue**        | Native     | `v-bind` / `ref`               | `v-on`                     | `isCustomElement` compiler option |
| **Svelte**     | Native     | `bind:this`                    | `on:event`                 | None                              |
| **React 19+**  | Native     | `ref`                          | `ref` + `addEventListener` | None                              |
| **React 18**   | Native     | `ref` only                     | `ref` + `addEventListener` | TypeScript declarations           |
| **Angular**    | `[attr.x]` | `ViewChild` + `.nativeElement` | `(event)`                  | `CUSTOM_ELEMENTS_SCHEMA`          |
