# AddToCalendar
[](https://hex.pm/packages/add_to_calendar)
[](https://hex.pm/packages/add_to_calendar)
[](https://hexdocs.pm/add_to_calendar)
[](https://gitlab.com/roelandvanbatenburg/add_to_calendar/-/pipelines)
[](https://gitlab.com/roelandvanbatenburg/add_to_calendar/-/pipelines)
[](LICENSE)
Server-side calendar links and ICS generation for Phoenix LiveView apps.
Generates Google Calendar / Outlook.com URLs and downloadable `.ics` files
from plain Elixir `Date` / `Time` structs — no JavaScript dependency, no
timezone database required.
## Installation
Add `add_to_calendar` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:add_to_calendar, "~> 0.1.0"}
]
end
```
`phoenix_live_view` is an optional dependency — only required if you use
`AddToCalendar.Component`.
## Usage
### Phoenix component
`AddToCalendar.Component.calendar_button/1` renders a ready-made dropdown
using a native `<details>/<summary>` element — no JavaScript required.

```heex
<AddToCalendar.Component.calendar_button
name={@event.title}
start_date={@event.starts_on}
start_time={@event.start_time}
end_date={@event.ends_on}
end_time={@event.end_time}
timezone={@event.timezone}
location={@full_address}
description={@event.description}
/>
```
The `:providers` attr controls which options appear, in order:
- `"google"` — Google Calendar (opens in new tab)
- `"outlook"` — Outlook.com (opens in new tab)
- `"apple"` — iCal download labelled "Apple Calendar"
- `"ical"` — iCal download labelled "iCal / Other"
If using Tailwind CSS, add this package to your content paths so its class
names are included in the build:
```js
content: ["../deps/add_to_calendar/lib/**/*.ex"]
```
#### Styling
- `class` — applied to the outer `<details>` wrapper.
- `button_class` — applied to the `<summary>` trigger, replacing the default
(`rounded px-2.5 py-1 text-sm font-medium ring-1 ring-inset ring-current
hover:bg-black/5`) rather than merging with it.
- `icon` — set to `false` to hide the calendar glyph on the trigger.
```heex
<AddToCalendar.Component.calendar_button
...
class="inline-block"
button_class="rounded-full bg-indigo-600 px-3 py-1.5 text-sm font-medium text-white hover:bg-indigo-500"
/>
```
The dropdown panel and its links aren't exposed as attrs — their classes
(`bg-white`, `border-gray-200`, `text-gray-700`, etc.) are fixed. If you need
to restyle the panel itself, wrap the component, copy `component.ex` into
your app or create a MR fixing this.
### Without Phoenix component
```elixir
event = %AddToCalendar.Event{
name: "Pickleball League",
start_date: ~D[2026-07-15],
start_time: ~T[09:00:00],
end_date: ~D[2026-07-15],
end_time: ~T[11:00:00],
timezone: "Pacific/Auckland",
location: "123 Main St, Auckland",
description: "Weekly round-robin"
}
AddToCalendar.google_url(event)
# => "https://calendar.google.com/calendar/render?..."
AddToCalendar.outlook_url(event)
# => "https://outlook.live.com/calendar/0/action/compose?..."
AddToCalendar.ics_content(event)
# => "BEGIN:VCALENDAR\r\n..."
AddToCalendar.ics_data_uri(event)
# => "data:text/calendar;charset=utf-8;base64,..."
```
## Development
Toolchain versions are pinned in `.tool-versions` (compatible with
[mise](https://mise.jdx.dev) or asdf).
```
mix deps.get
mix check
```
A `Dockerfile` is provided for a reproducible environment matching
`.tool-versions`:
```
docker build -t add_to_calendar .
docker run --rm add_to_calendar
```
### Previewing `AddToCalendar.Component`
This is a standalone library, so there's no host app to render `calendar_button/1` in a browser
while iterating on its styling. A dev-only [Phoenix Storybook](https://hexdocs.pm/phoenix_storybook)
app under `dev/` fills that gap, with stories under `dev/storybook/`.
```
mix phx.server
```
Then visit <http://localhost:4000>. Elixir changes (e.g. to `component.ex`) and Tailwind
CSS changes both live-reload the page. This tooling only exists for `MIX_ENV=dev` (see
`elixirc_paths` and the `application/0` mod in `mix.exs`) and is excluded from the published
package.
## License
[MIT](LICENSE)