---
title: "Web component"
url: "https://twistcal.com/docs/web-component"
description: "The twist-cal custom element — all attributes, calendar selection, JSON event data, all-day events, and the twistcal:add callback."
---

Drop `<twist-cal>` on the page. It renders a button with a dropdown menu. All styles live in a Shadow DOM root — no bleed in either direction.

```
<script defer src="https://cdn.jsdelivr.net/npm/@freshjuice/twistcal/dist/twistcal.min.js"></script>

<twist-cal
  title="Product Launch Webinar"
  start="2026-02-10T14:00:00"
  end="2026-02-10T15:30:00"
  location="Online"
  description="Join us for the launch of TwistCal v1.">
</twist-cal>
```

## Attributes

| Attribute | Required | Description |
| --- | --- | --- |
| title | yes | Event title (calendar subject) |
| start | yes | Start datetime — any value new Date() understands |
| end | yes | End datetime |
| description | no | Event description (calendar body) |
| location | no | Event location |
| url | no | Event URL (included in .ics) |
| timezone | no | IANA timezone (e.g. Europe/Oslo). Naive start/end are interpreted as wall-clock in that zone, converted to UTC. DST-aware. Ignored if start/end already carry an offset. See Datetime. |
| label | no | Button text. Default: auto-detected from lang or Add to Calendar |
| lang | no | Language code. Auto-detected from <html lang> or navigator.language if omitted. See i18n. |
| variant | no | solid (default) or outline (white bg, dark border, dark text) |
| show-icons | no | true (default) or false — show/hide brand icons in dropdown |
| show-icon | no | true (default) or false — show/hide the calendar icon on the button |
| calendars | no | Comma-separated list of calendars to show, in order: google,outlook,yahoo,ics. Default: all four. ics and ical both work. Alias: services. |
| all-day | no | true or false (default). Date-only .ics and Google/Yahoo formats. |
| branding | no | true (default) or false — show/hide “Powered by TwistCal” link in dropdown |

## Calendar selection

Show only the calendars you want, in the order you want:

```
<!-- Google + Apple Calendar only -->
<twist-cal calendars="google,ics" title="..." start="..." end="...">
</twist-cal>

<!-- Single calendar — effectively a download button -->
<twist-cal calendars="ics" label="Download .ics" title="..." start="..." end="...">
</twist-cal>
```

## Rich data via JSON

For long descriptions or structured data, pass the event as JSON inside the element:

```
<twist-cal label="Add to my calendar">
  <script type="application/json">
  {
    "title": "Quarterly Review — Q1 2026",
    "start": "2026-03-15T09:00:00",
    "end": "2026-03-15T10:00:00",
    "location": "Conference Room B, 4th Floor",
    "description": "Bring your Q1 metrics.\nDial-in: +1-555-0100, code 4521#",
    "url": "https://twistcal.com/demo"
  }
  </script>
</twist-cal>
```

JSON overrides matching attributes. Attributes not in the JSON are still read from the element.

## All-day events

Set `all-day="true"` for date-only formats:

```
<twist-cal
  all-day="true"
  title="Company offsite"
  start="2026-04-20"
  end="2026-04-22">
</twist-cal>
```

Produces date-only `.ics` (`VALUE:DATE`) and Google/Yahoo all-day URLs.

## `twistcal:add` callback

Each click dispatches a `twistcal:add` `CustomEvent` on the `<twist-cal>` element with `{ action, event }` detail:

```
<twist-cal id="cal" title="..." start="..." end="..."></twist-cal>

<script>
  document.getElementById('cal').addEventListener('twistcal:add', (e) => {
    console.log('Added to:', e.detail.action, e.detail.event);
  });
</script>
```

`action` is `google`, `outlook`, `yahoo`, `ics`, or a custom calendar id. The event also fires on `document` for [declarative triggers](/docs/triggers/).

## Next

-   [Styling](/docs/styling/) — CSS custom properties and theming
-   [Declarative triggers](/docs/triggers/) — use your own buttons
-   [API](/docs/api/) — programmatic access