Использование с Svelte

Интеграция Flatpickr в Svelte строится вокруг идеи: DOM-ориентированная библиотека управляет конкретным input-элементом, а Svelte отвечает за реактивность и жизненный цикл компонента. Основная сложность заключается не в инициализации, а в корректном разрушении экземпляра и синхронизации состояния.

Flatpickr работает напрямую с DOM, поэтому в Svelte его подключение всегда выполняется внутри onMount, когда элемент уже существует в DOM-дереве.

<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";

  let input;
  let picker;

  onMount(() => {
    picker = flatpickr(input, {
      dateFormat: "Y-m-d",
    });

    return () => {
      picker.destroy();
    };
  });
</script>

<input bind:this={input} type="text" />

Ключевой момент: bind:this передаёт ссылку на реальный DOM-элемент, который Flatpickr использует как точку привязки.


Базовая реактивная связка значения

В Svelte состояние обычно хранится в переменных. Flatpickr не знает о реактивности, поэтому требуется вручную синхронизировать значение.

<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";

  let input;
  let picker;
  let date = "";

  onMount(() => {
    picker = flatpickr(input, {
      dateFormat: "Y-m-d",
      defaultDate: date,
      onChange: (selectedDates, dateStr) => {
        date = dateStr;
      }
    });

    return () => picker.destroy();
  });
</script>

<input bind:this={input} bind:value={date} />
<p>Выбранная дата: {date}</p>

Здесь происходит двусторонняя логика:

  • Flatpickr обновляет date через onChange
  • Svelte может обновлять input через bind:value

Обновление значения извне

Реактивность Svelte позволяет менять дату без взаимодействия с календарём, но Flatpickr нужно уведомлять вручную.

<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";

  let input;
  let picker;
  let date = "2026-01-01";

  onMount(() => {
    picker = flatpickr(input, {
      defaultDate: date,
      onChange: (_, d) => date = d
    });

    return () => picker.destroy();
  });

  $: if (picker && date) {
    picker.setDate(date, true);
  }
</script>

<input bind:this={input} bind:value={date} />

Конструкция $: обеспечивает синхронизацию при изменении date.


Оборачивание Flatpickr в Svelte action

Наиболее чистый способ интеграции — использование actions. Это позволяет инкапсулировать логику и не засорять компонент.

// flatpickrAction.js
import flatpickr from "flatpickr";

export function flatpickrAction(node, options = {}) {
  const instance = flatpickr(node, options);

  return {
    upd ate(newOptions) {
      instance.se t(options = newOptions);
    },
    destroy() {
      instance.destroy();
    }
  };
}

Использование:

<script>
  import { flatpickrAction } from "./flatpickrAction";

  let date;
</script>

<input use:flatpickrAction={{ dateFormat: "Y-m-d" }} bind:value={date} />

Action-подход делает Flatpickr частью декларативного Svelte-кода, минимизируя ручное управление жизненным циклом.


Двусторонняя синхронизация через store

В более сложных приложениях состояние часто выносится в store.

// dateStore.js
import { writable } from "svelte/store";

export const dateStore = writable("");
<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";
  import { dateStore } from "./dateStore";

  let input;
  let picker;

  let unsubscribe;

  onMount(() => {
    unsubscribe = dateStore.subscribe(value => {
      if (picker) picker.setDate(value, false);
    });

    picker = flatpickr(input, {
      onChange: (_, d) => dateStore.set(d)
    });

    return () => {
      unsubscribe();
      picker.destroy();
    };
  });
</script>

<input bind:this={input} />

Store выступает единым источником истины, а Flatpickr становится лишь интерфейсом ввода.


Работа с диапазонами дат

Flatpickr поддерживает range mode, что важно для интерфейсов бронирования и фильтрации.

<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";

  let input;
  let picker;
  let range = [];

  onMount(() => {
    picker = flatpickr(input, {
      mode: "range",
      onChange: (selectedDates) => {
        range = selectedDates;
      }
    });

    return () => picker.destroy();
  });
</script>

<input bind:this={input} />

Особенность Svelte: массив range обновляется реактивно, что позволяет легко привязывать его к UI-фильтрам.


Использование с TypeScript

При работе в Svelte + TypeScript важно типизировать экземпляр Flatpickr.

<script lang="ts">
  import { onMount } from "svelte";
  import flatpickr, { Instance } from "flatpickr";

  let input: HTMLInputElement;
  let picker: Instance | null = null;

  onMount(() => {
    picker = flatpickr(input, {
      dateFormat: "Y-m-d"
    });

    return () => picker?.destroy();
  });
</script>

Тип Instance предотвращает ошибки при вызове методов setDate, clear, destroy.


Интеграция с SvelteKit и SSR

При использовании SvelteKit важно учитывать, что серверный рендеринг не имеет доступа к window и DOM.

Flatpickr должен загружаться только на клиенте:

<script>
  import { onMount } from "svelte";

  let input;

  onMount(async () => {
    const flatpickr = (await import("flatpickr")).default;

    flatpickr(input, {
      dateFormat: "Y-m-d"
    });
  });
</script>

<input bind:this={input} />

Динамический import гарантирует отсутствие ошибок SSR.


Кастомизация тем и стилей

Flatpickr предоставляет CSS-базу, которую можно переопределять.

import "flatpickr/dist/flatpickr.css";
import "flatpickr/dist/themes/dark.css";

В Svelte стили часто переопределяются локально:

<style>
  :global(.flatpickr-calendar) {
    border-radius: 12px;
  }

  :global(.flatpickr-day.selected) {
    background: #4f46e5;
  }
</style>

Управление несколькими инстансами

В Svelte можно легко создавать несколько независимых календарей.

<script>
  import { onMount } from "svelte";
  import flatpickr from "flatpickr";

  let inputs = [];
  let pickers = [];

  onMount(() => {
    pickers = inputs.map(input =>
      flatpickr(input, { dateFormat: "Y-m-d" })
    );

    return () => pickers.forEach(p => p.destroy());
  });
</script>

<input bind:this={el => inputs[0] = el} />
<input bind:this={el => inputs[1] = el} />

Проблема повторной инициализации и её решение

В реактивных компонентах Svelte важно избегать повторного создания Flatpickr при каждом обновлении состояния.

Правильный подход:

  • инициализация только в onMount
  • обновление через API setDate, set
  • уничтожение через destroy

Неправильный подход:

  • создание Flatpickr внутри $: реактивных блоков

Использование custom input компонента

Flatpickr можно комбинировать с компонентной архитектурой Svelte.

<!-- DateInput.svelte -->
<script>
  export let value = "";
  export let input;
</script>

<input bind:this={input} bind:value />
<script>
  import DateInput from "./DateInput.svelte";
  import flatpickr from "flatpickr";
  import { onMount } from "svelte";

  let input;
  let date;

  onMount(() => {
    flatpickr(input, {
      onChange: (_, d) => date = d
    });
  });
</script>

<DateInput bind:input />

Поведение в реактивных формах

При использовании Svelte форм важно помнить, что Flatpickr не интегрирован с нативной системой bind:group или form автоматически. Все изменения должны синхронизироваться вручную через события.

Рекомендуемая схема:

  • Flatpickr → обновляет store или переменную
  • Svelte → обновляет UI
  • внешние изменения → вызывают setDate

Производительность и жизненный цикл

Flatpickr создаёт DOM-структуру календаря вне Svelte tree. Это означает:

  • Svelte не отслеживает внутренние изменения календаря
  • производительность зависит от минимального количества пересозданий
  • важно избегать частых destroy/init циклов

Оптимальная стратегия — один экземпляр на input и управление через API, а не пересоздание.