Calendarkit Pro V2
Customization

Custom Views

Add custom views to the header switcher and sidebar alongside the built-in views.

Custom Views

The customViews prop lets you extend the calendar with fully custom view panels. Each custom view gets a button in the header view switcher (after the built-in views) and a link in the sidebar's "Operations" section.

Interface

interface CustomView {
  id:         string;        // unique — must not collide with built-in view names
  label:      string;        // text shown in button and sidebar
  icon?:      React.ReactNode; // optional icon for the button/sidebar row
  component:  React.ReactNode; // rendered when this view is active
}

Basic example

import { BarChart2, Users } from 'lucide-react';

const customViews: CustomView[] = [
  {
    id: 'analytics',
    label: 'Analytics',
    icon: <BarChart2 size={14} />,
    component: (
      <div className="p-6">
        <h2 className="text-xl font-semibold mb-4">Analytics</h2>
        <p className="text-muted-foreground">
          Your analytics dashboard goes here.
        </p>
      </div>
    ),
  },
  {
    id: 'team',
    label: 'Team',
    icon: <Users size={14} />,
    component: <TeamDashboard />,
  },
];

<ProScheduler customViews={customViews} ... />

Full example

'use client';

import { useState } from 'react';
import { ProScheduler } from 'calendarkit-prov2';
import type { CalendarEvent, CustomView } from 'calendarkit-prov2';

// Example analytics component
function AnalyticsPanel({ events }: { events: CalendarEvent[] }) {
  const byDay = [0, 1, 2, 3, 4, 5, 6].map((d) => ({
    day: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][d],
    count: events.filter((e) => new Date(e.start).getDay() === d).length,
  }));

  return (
    <div className="p-6 space-y-6">
      <h2 className="text-2xl font-semibold">Analytics</h2>
      <p className="text-muted-foreground">Total events: {events.length}</p>
      <div className="grid grid-cols-7 gap-2">
        {byDay.map(({ day, count }) => (
          <div key={day} className="flex flex-col items-center gap-1">
            <div
              className="w-full bg-blue-500 rounded-sm min-h-[4px] transition-all"
              style={{ height: `${Math.max(count * 20, 4)}px` }}
            />
            <span className="text-xs text-muted-foreground">{day}</span>
            <span className="text-xs font-medium">{count}</span>
          </div>
        ))}
      </div>
    </div>
  );
}

// Example team component
function TeamPanel() {
  const members = [
    { name: 'Alice Johnson', role: 'Designer',  avatar: '👩‍🎨' },
    { name: 'Bob Smith',     role: 'Developer', avatar: '👨‍💻' },
    { name: 'Carol White',   role: 'Manager',   avatar: '👩‍💼' },
  ];
  return (
    <div className="p-6 space-y-4">
      <h2 className="text-2xl font-semibold">Team</h2>
      {members.map((m) => (
        <div key={m.name} className="flex items-center gap-3 p-3 rounded-lg border">
          <span className="text-2xl">{m.avatar}</span>
          <div>
            <p className="font-medium">{m.name}</p>
            <p className="text-sm text-muted-foreground">{m.role}</p>
          </div>
        </div>
      ))}
    </div>
  );
}

// SVG icons for the view buttons
const BarChartIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
       fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
    <line x1="18" y1="20" x2="18" y2="10"/>
    <line x1="12" y1="20" x2="12" y2="4"/>
    <line x1="6"  y1="20" x2="6"  y2="14"/>
  </svg>
);

const TeamIcon = () => (
  <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24"
       fill="none" stroke="currentColor" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round">
    <path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/>
    <circle cx="9" cy="7" r="4"/>
    <path d="M23 21v-2a4 4 0 00-3-3.87"/>
    <path d="M16 3.13a4 4 0 010 7.75"/>
  </svg>
);

export default function CustomViewsExample() {
  const [events, setEvents] = useState<CalendarEvent[]>([]);

  // Build customViews inside the component so analytics panel gets live events
  const customViews: CustomView[] = [
    {
      id: 'analytics',
      label: 'Analytics',
      icon: <BarChartIcon />,
      component: <AnalyticsPanel events={events} />,
    },
    {
      id: 'team',
      label: 'Team',
      icon: <TeamIcon />,
      component: <TeamPanel />,
    },
  ];

  return (
    <div className="h-[700px] border rounded-xl overflow-hidden">
      <ProScheduler
        view="week"
        events={events}
        date={new Date()}
        customViews={customViews}
        onEventCreate={(e) =>
          setEvents((prev) => [...prev, { ...e, id: crypto.randomUUID() } as CalendarEvent])
        }
        onEventUpdate={(updated) =>
          setEvents((prev) => prev.map((e) => (e.id === updated.id ? updated : e)))
        }
        onEventDelete={(id) =>
          setEvents((prev) => prev.filter((e) => e.id !== id))
        }
      />
    </div>
  );
}

On this page