It is not necessary to use a framework with Cally. However, by virtue of being written as web components, Cally is framework-agnostic and can be used anywhere.
Most frameworks support web components out of the box, requiring little-to-no setup. Here we will walk through how to use Cally in React, Vue, and Svelte. The process should be similar for other frameworks.
React
As of version 19, React has excellent support for web components. They can now be used directly, with no wrappers or setup necessary.
import { useState } from "react";
import { createRoot } from 'react-dom/client';
import "cally";
function App() {
const [value, setValue] = useState("");
return (
<>
<p>Value is: {value}</p>
<calendar-range
value={value}
onchange={(event) => setValue(event.target.value)}
>
<calendar-month />
<calendar-month offset={1} />
</calendar-range>
</>
);
}
const root = createRoot(document.getElementById("root"));
root.render(<App />)
Note: React requires that event listeners for web components
use the event name verbatim. For instance if you have an
event named fooBar
in your web component, you must listen for the
onfooBar
event. This is unusual for React, which typically expects
events to be like onFooBar
.
TypeScript
If you are using TypeScript, you can augment React's types to add type-checking and improve your editor experience. Cally exports types for each component's props making this a simple, one-time procedure.
First you should create a d.ts
file in your React project, with
any name you like. For this example let's call it globals.d.ts
.
In that file, paste the following code:
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
CalendarMultiProps,
} from "cally";
type MapEvents<T> = {
[K in keyof T as K extends `on${infer E}` ? `on${Lowercase<E>}` : K]: T[K];
};
declare module "react" {
namespace JSX {
interface IntrinsicElements {
"calendar-month": MapEvents<CalendarMonthProps> &
React.HTMLAttributes<HTMLElement>;
"calendar-range": MapEvents<CalendarRangeProps> &
React.HTMLAttributes<HTMLElement>;
"calendar-date": MapEvents<CalendarDateProps> &
React.HTMLAttributes<HTMLElement>;
"calendar-multi": MapEvents<CalendarMultiProps> &
React.HTMLAttributes<HTMLElement>;
}
}
}
This uses TypeScript's declaration merging feature. You can read more about it in the TypeScript Handbook.
Finally, you must add this to the compilerOptions.types
field in
your
tsconfig.json
:
{
// ...
"compilerOptions": {
// ...
"types": ["./globals.d.ts"]
}
}
Vue
Vue has excellent support for web components. If you haven't already, you need to configure vue to understand web components. After that, they can be used directly.
<script setup>
import 'cally';
</script>
<template>
<calendar-range :months="2">
<calendar-month />
<calendar-month :offset="1" />
</calendar-range>
</template>
Usage with v-model
The <calendar-date>
and <calendar-range>
components
emit change
events when their value changes. You can use the
v-model
directive to bind ref
s to these events.
As noted in the Vue docs, v-model
listens for
input
events by default. But by using the
.lazy
modifier, it will listen for change
events.
<script setup>
import 'cally';
const selected = ref("")
</script>
<template>
<p>Selected range: {{ selected }}</p>
<calendar-range :months="2" v-model.lazy="selected">
<calendar-month />
<calendar-month :offset="1" />
</calendar-range>
</template>
You are not required to use v-model
. You can listen for events
yourself if you prefer:
<script setup>
import 'cally';
const selected = ref("")
function onChange(event) {
selected.value = event.target.value
}
</script>
<template>
<p>Selected range: {{ selected }}</p>
<calendar-range :months="2" :value="selected" @change="onChange">
<calendar-month />
<calendar-month :offset="1" />
</calendar-range>
</template>
TypeScript
If you are using TypeScript, you can augment Vue's types to add type-checking and improve your editor experience. Cally exports types for each component's props making this a simple, one-time procedure.
First you should create a d.ts
file in your Vue project, with any
name you like. For this example let's call it globals.d.ts
. In
that file, paste the following code:
import type { DefineComponent } from "vue";
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
} from "cally";
interface CallyComponents {
"calendar-range": DefineComponent<CalendarRangeProps>;
"calendar-date": DefineComponent<CalendarDateProps>;
"calendar-month": DefineComponent<CalendarMonthProps>;
}
declare module "vue" {
interface GlobalComponents extends CallyComponents {}
}
declare global {
namespace JSX {
interface IntrinsicElements extends CallyComponents {}
}
}
This uses TypeScript's declaration merging feature. You can read more about it in the TypeScript Handbook.
Finally, you must add this to the compilerOptions.types
field in
your
tsconfig.json
:
{
// ...
"compilerOptions": {
// ...
"types": ["./globals.d.ts"]
}
}
Now you will get type-checking for both props and events, along with auto-complete in your editor.
Svelte
Svelte has excellent support for web components. There is no setup required to start using Web Components.
<script lang="ts">
import "cally";
</script>
<calendar-range months={2}>
<calendar-month></calendar-month>
<calendar-month offset={1}></calendar-month>
</calendar-range>
TypeScript
If you are using TypeScript, you can augment Svelte's types to add type-checking and improve your editor experience. Cally exports types for each component's props making this a simple, one-time procedure.
First you should create a d.ts
file in your Svelte project, with
any name you like. For this example let's call it globals.d.ts
.
In that file, paste the following code:
import type {
CalendarRangeProps,
CalendarMonthProps,
CalendarDateProps,
} from "cally";
type MapEvents<T> = {
[K in keyof T as K extends `on${infer E}` ? `on:${Lowercase<E>}` : K]: T[K];
};
declare module "svelte/elements" {
interface SvelteHTMLElements {
"calendar-range": MapEvents<CalendarRangeProps>;
"calendar-month": MapEvents<CalendarMonthProps>;
"calendar-date": MapEvents<CalendarDateProps>;
}
}
This uses TypeScript's declaration merging feature. You can read more about it in the TypeScript Handbook.
Finally, you must add this to the compilerOptions.types
field in
your
tsconfig.json
:
{
// ...
"compilerOptions": {
// ...
"types": ["./globals.d.ts"]
}
}
Now you will get type-checking for both props and events, along with auto-complete in your editor.