Разработка календаря событий: FullCalendar, API, экспорт, мобильный UX

Создание календаря событий — это не просто вывод данных в сетку: повторяющиеся события, часовые пояса и мобильный UX превращают задачу в настоящий квест. Мы разрабатываем календари под ключ, интегрируя FullCalendar, Google Calendar API и экспорт iCal, чтобы решить эти сложности. Наша команда берёт на себя весь цикл — от проектирования до поддержки, обеспечивая надёжное решение, которое удобно на любом устройстве.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка календаря событий: FullCalendar, API, экспорт, мобильный UX
Средний
~5 дней
Часто задаваемые вопросы

Какие сложности возникают при создании календаря?

Создание интерактивного календаря событий — задача, которая на первый взгляд кажется тривиальной: вытащить события из базы и отобразить в месячной сетке. На практике проект обрастает техническими нюансами, способными похоронить бюджет и сроки. Мы реализовали более 20 таких проектов за 10 лет, от простого списка до корпоративного планировщика с drag-and-drop, повторяющимися событиями и интеграцией Google Calendar. Разберём ключевые подводные камни.

Первая сложность — повторяющиеся события. Простой event loop не подходит: нужна поддержка исключений, переносов, бесконечных серий (cron-подобные правила). Вторая — часовые пояса. Хранить даты в UTC, отображать в локальном времени — классика, но если событие создаётся в одном TZ, а просматривается в другом, даты съезжают. Третья — мобильный UX. Полноценный календарь на экране 375×667 px — отдельная задача: требуются свайпы, адаптивная сетка, скролл и touch-интерфейс. Игнорирование этих аспектов приводит к высокому bounce rate на смартфонах.

Какую библиотеку выбрать?

Выбор библиотеки для календаря — компромисс между функциональностью и бандлом. FullCalendar в 2 раза функциональнее React Big Calendar по числу плагинов. Мы используем FullCalendar для 90% проектов. Он покрывает месячный, недельный, дневной и списочный виды, drag-and-drop, повторяющиеся события, локализацию. React Big Calendar в два раза легче, но не имеет встроенной поддержки повторяющихся событий и экспорта. TOAST UI Calendar — достойная альтернатива с корейским корнями, но сообщество меньше.

Библиотека Плагины Поддержка React/Vue Повторяющиеся события Бандл (min+gzip)
FullCalendar 20+ Оба rrule 40-60 KB
React Big Calendar Мало React Нет 20-30 KB
TOAST UI Calendar Средне Vue, React (beta) Есть 30-50 KB

Если нужно просто вывести список событий по месяцам без интерактива — хватит чистого CSS Grid. Но для drag-and-drop, модалок и экспорта iCal библиотека обязательна.

Как мы интегрируем FullCalendar с React

Установка и настройка занимают 30 минут. Базовый компонент с переключением видов, локализацией и кастомным рендером:

Установка FullCalendar
npm install @fullcalendar/react @fullcalendar/core @fullcalendar/daygrid \
 @fullcalendar/timegrid @fullcalendar/list @fullcalendar/interaction 
import FullCalendar from '@fullcalendar/react'
import dayGridPlugin from '@fullcalendar/daygrid'
import timeGridPlugin from '@fullcalendar/timegrid'
import listPlugin from '@fullcalendar/list'
import interactionPlugin from '@fullcalendar/interaction'
import { EventInput, DateSelectArg, EventClickArg } from '@fullcalendar/core'
import ruLocale from '@fullcalendar/core/locales/ru'

export function EventCalendar() {
  const [events, setEvents] = useState<EventInput[]>([])
  const [selectedEvent, setSelectedEvent] = useState<EventClickArg | null>(null)

  useEffect(() => {
    fetch('/api/events')
      .then(r => r.json())
      .then(data => setEvents(data.map(mapToFullCalendarEvent)))
  }, [])

  function handleEventClick(info: EventClickArg) {
    info.jsEvent.preventDefault()
    setSelectedEvent(info)
  }

  function handleDateSelect(info: DateSelectArg) {
    openCreateEventModal({ start: info.start, end: info.end, allDay: info.allDay })
  }

  return (
    <>
      <FullCalendar
        plugins={[dayGridPlugin, timeGridPlugin, listPlugin, interactionPlugin]}
        initialView="dayGridMonth"
        locale={ruLocale}
        headerToolbar={{
          left: 'prev,next today',
          center: 'title',
          right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek',
        }}
        buttonText={{
          today: 'Сегодня',
          month: 'Месяц',
          week: 'Неделя',
          day: 'День',
          list: 'Список',
        }}
        events={events}
        selectable={true}
        selectMirror={true}
        dayMaxEvents={3}
        weekends={true}
        eventClick={handleEventClick}
        select={handleDateSelect}
        eventTimeFormat={{
          hour: '2-digit',
          minute: '2-digit',
          hour12: false
        }}
        firstDay={1}
        height="auto"
        eventDisplay="block"
        eventContent={renderEventContent}
      />
      {selectedEvent && <EventModal event={selectedEvent} onClose={() => setSelectedEvent(null)} />}
    </>
  )
}

function renderEventContent(eventInfo: any) {
  return (
    <div className="fc-event-custom">
      <span className="fc-event-dot" style={{ background: eventInfo.event.backgroundColor }} />
      <span className="fc-event-title">{eventInfo.event.title}</span>
      {!eventInfo.event.allDay && (
        <span className="fc-event-time">{eventInfo.timeText}</span>
      )}
    </div>
  )
}

API на Laravel возвращает события с полями start, end, backgroundColor. Маппинг на фронте происходит через функцию mapToFullCalendarEvent. Мы используем Repository pattern для разделения логики запросов.

Как реализовать повторяющиеся события?

Для повторяющихся событий применяем библиотеку RRule — JavaScript-реализацию iCalendar RFC 5545. FullCalendar подхватывает правило и рассчитывает все вхождения на лету. Исключения — массив exdate:

npm install @fullcalendar/rrule rrule 
import rrulePlugin from '@fullcalendar/rrule'

const recurringEvent: EventInput = {
  id: 'weekly-standup',
  title: 'Еженедельный стендап',
  rrule: {
    freq: 'weekly',
    byweekday: ['mo', 'we', 'fr'],
    dtstart: new Date(),
    until: new Date(new Date().setFullYear(new Date().getFullYear() + 1)),
  },
  duration: '00:30',
  backgroundColor: '#0ea5e9',
}

const recurringWithExceptions: EventInput = {
  ...recurringEvent,
  exdate: [
    new Date(2025, 4, 1, 9, 0).toISOString(),
    new Date(2025, 5, 12, 9, 0).toISOString(),
  ],
}

Важно: даты в exdate должны быть в формате ISO с явным указанием часового пояса (Z или смещение). Часовые пояса решаем хранением в UTC и конвертацией через date-fns-tz на клиенте. FullCalendar поддерживает timeZone="local" или конкретный IANA-таймзон.

Как организован экспорт и интеграции?

Экспорт событий в iCal реализуем на бэкенде (Laravel/Nest.js). Для Google Calendar используем плагин @fullcalendar/google-calendar — достаточно указать ID календаря и API-ключ. Цветовая кодировка событий по календарям, фоновая подсветка — всё из коробки.

Интеграция Плагин FullCalendar Сложность Примечание
Google Calendar @fullcalendar/google-calendar Низкая ID + API-ключ
iCal (экспорт) Бэкенд Средняя Генерация .ics
Drag-and-drop @fullcalendar/interaction Низкая Встроенное

Что входит в реализацию календаря?

  • Документация: архитектура API, схема событий, инструкция для редакторов.
  • Доступы: настройка окружения, CI/CD, переменные окружения.
  • Обучение: 2 часа демо для команды.
  • Поддержка: 30 дней бесплатных правок после деплоя.
  • Адаптив: полная поддержка мобильных (swipe, compact view).
  • SEO: события доступны по прямым URL, добавляются в sitemap.

Процесс работы

  1. Аналитика (2–3 дня): разбираем источники событий, типы повторов, требования к мобильности.
  2. Проектирование (3–5 дней): определяем стек, прототип API, модель данных.
  3. Разработка (10–20 дней): бэкенд (Laravel/Node.js), фронтенд (React/Vue), интеграции.
  4. Тестирование (3–5 дней): unit + integration тесты, ручное тестирование на мобильных.
  5. Деплой (1 день): настройка кеширования, Cloudflare, мониторинг.

Сроки и стоимость

Статичный календарь (вывод событий из API) — 1 день. Полноценный с месяц/неделя/список, модалкой, фильтром по категориям — 3–4 дня. С drag-and-drop, повторяющимися событиями, iCal-экспортом и Google Calendar — от 2 недель. Стоимость рассчитывается индивидуально: напишите нам, оценим ваш проект за 1 день. Получите консультацию — обсудим детали интеграции.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060