diff --git a/CHANGELOG.md b/CHANGELOG.md
index fee600b..bf1233b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
---
+## [1.3.0] - 2026-03-29
+
+### Updated
+
+- timepicker-ui dependency from 4.2.1 to 4.3.0
+
+### Added
+
+- Re-exported `TemplateProvider` type - interface for plugin `templateProvider` hook
+- Re-exported `ClearHandler` type - interface for plugin `clearHandler` hook
+
+### Notes
+
+- Plugins now support a `templateProvider` hook to register their own modal templates without core static imports
+- Plugins now support a `clearHandler` hook to register custom clear logic executed when the clear button is pressed
+- Plugin system refactored for true tree-shaking: unused plugins (wheel, range, timezone) are completely excluded from the final bundle when not imported
+- `PluginRegistry.getTemplateProvider()` and `PluginRegistry.getClearHandler()` methods now available on the re-exported `PluginRegistry`
+
+---
+
## [1.2.0] - 2026-03-24
### Added
diff --git a/README.md b/README.md
index 84ff16e..63cfb13 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,21 @@
# timepicker-ui-react
-Official React wrapper for [timepicker-ui](https://github.com/pglejzer/timepicker-ui) v4.x.
+Official React wrapper for [timepicker-ui](https://github.com/pglejzer/timepicker-ui) v4.x - clock & wheel time picker, full TypeScript, SSR-safe.
[](https://badge.fury.io/js/timepicker-ui-react)
[](https://img.shields.io/npm/l/timepicker-ui-react)
-[](https://bundlephobia.com/package/timepicker-ui-react)
+[](https://npmcharts.com/compare/timepicker-ui-react?minimal=true)
-A lightweight, SSR-safe React component that provides a thin wrapper around the powerful timepicker-ui library with full TypeScript support.
+[Live Demo](https://timepicker-ui.vercel.app/react) • [Documentation](https://timepicker-ui.vercel.app/react) • [Changelog](./CHANGELOG.md) • [Core Library](https://github.com/pglejzer/timepicker-ui)
-**[View Live Documentation](https://timepicker-ui.vercel.app/react)** • **[Try Interactive Demo](https://timepicker-ui.vercel.app/react/examples)**
+## Why timepicker-ui-react?
-## Features
-
-- **Full TypeScript Support** - All types directly from timepicker-ui core
-- **SSR-Safe** - Works with Next.js, Remix, Gatsby, and other SSR frameworks
-- **Zero Type Duplication** - Re-exports core types, no duplicated interfaces
-- **Event-Driven** - Direct mapping to timepicker-ui's EventEmitter API
-- **Controlled & Uncontrolled** - Support for both value patterns
-- **ESM Only** - Modern, tree-shakeable bundle
+- **Thin wrapper** - all power comes from the battle-tested timepicker-ui core
+- **Zero type duplication** - types re-exported directly from `timepicker-ui`
+- **SSR-safe** - works in Next.js, Remix, Astro out of the box
+- **Controlled & uncontrolled** - both `value` and `defaultValue` patterns
+- **All input props** - extends `InputHTMLAttributes`, pass anything directly
+- **Plugin support** - Timezone, Range, Wheel via `PluginRegistry`
## Installation
@@ -25,421 +23,141 @@ A lightweight, SSR-safe React component that provides a thin wrapper around the
npm install timepicker-ui-react
```
-```bash
-yarn add timepicker-ui-react
-```
-
-```bash
-pnpm add timepicker-ui-react
-```
+> `timepicker-ui` is included as a dependency - no need to install it separately.
-> **Note:** `timepicker-ui` is automatically installed as a dependency, no need to install it separately.
-
-## Usage
-
-### Basic Example
+## Quick Start
```tsx
-import React from "react";
import { Timepicker } from "timepicker-ui-react";
+import "timepicker-ui/main.css";
function App() {
- const handleConfirm = (data) => {
- console.log("Time confirmed:", data);
- };
-
- return ;
-}
-```
-
-### With Options
-
-```tsx
-import React from "react";
-import { Timepicker, type TimepickerOptions } from "timepicker-ui-react";
-
-function App() {
- const options: TimepickerOptions = {
- clock: {
- type: "24h",
- autoSwitchToMinutes: true,
- },
- ui: {
- theme: "m3-green",
- mobile: false,
- },
- labels: {
- ok: "Confirm",
- cancel: "Close",
- },
- };
-
- return ;
-}
-```
-
-### Controlled Component
-
-```tsx
-import React, { useState } from "react";
-import { Timepicker } from "timepicker-ui-react";
-
-function App() {
- const [time, setTime] = useState("12:00 AM");
-
return (
{
- setTime(`${data.hour}:${data.minutes} ${data.type}`);
- }}
- onConfirm={(data) => {
- console.log("Confirmed:", data);
- }}
+ placeholder="Select time"
+ onConfirm={(data) => console.log("Selected:", data)}
/>
);
}
```
-### With All Callbacks
-
-```tsx
-import React from "react";
-import { Timepicker } from "timepicker-ui-react";
-
-function App() {
- return (
- console.log("Opened:", data)}
- onConfirm={(data) => console.log("Confirmed:", data)}
- onCancel={() => console.log("Cancelled")}
- onUpdate={(data) => console.log("Updated:", data)}
- onSelectHour={(data) => console.log("Hour selected:", data)}
- onSelectMinute=(data) => console.log("Minute selected:", data)}
- onSelectAM={() => console.log("AM selected")}
- onSelectPM={() => console.log("PM selected")}
- onError={(data) => console.log("Error:", data)}
- />
- );
-}
-```
-
-### Using Plugins (Timezone & Range)
-
-> **Important:** Timezone and Range features are **plugins** that must be manually imported and registered. They are **not included by default** for tree-shaking optimization.
-
-```tsx
-"use client";
-
-import React, { useEffect } from "react";
-import { Timepicker, PluginRegistry } from "timepicker-ui-react";
-
-function App() {
- useEffect(() => {
- // Register plugins once when component mounts
- const registerPlugins = async () => {
- const { TimezonePlugin } = await import("timepicker-ui/plugins/timezone");
- const { RangePlugin } = await import("timepicker-ui/plugins/range");
-
- PluginRegistry.register(TimezonePlugin);
- PluginRegistry.register(RangePlugin);
- };
-
- registerPlugins();
- }, []);
-
- return (
- <>
- {/* Timezone example */}
- console.log("Timezone changed:", data)}
- />
-
- {/* Range example */}
- console.log("Range confirmed:", data)}
- onRangeSwitch={(data) => console.log("Range switch:", data)}
- onRangeValidation={(data) => console.log("Range validation:", data)}
- />
- >
- );
-}
-```
-
-**Why plugins must be registered manually:**
-
-- **Tree-shaking** - Bundle only what you use
-- **Performance** - Avoid loading unnecessary code
-- **SSR compatibility** - Plugins load on client-side only
-
-### SSR (Next.js Example)
-
-```tsx
-"use client";
-
-import { Timepicker } from "timepicker-ui-react";
-
-export default function TimepickerPage() {
- return (
-
-
Select Time
-
-
- );
-}
-```
-
-The component is SSR-safe by default and will render a basic input during server-side rendering, then hydrate with the full timepicker on the client.
-
## API
-### `TimepickerProps`
-
-| Prop | Type | Description |
-| ------------------- | --------------------------------------- | ------------------------------------------ |
-| `options` | `TimepickerOptions` | Full configuration from timepicker-ui core |
-| `value` | `string` | Controlled value |
-| `defaultValue` | `string` | Default value for uncontrolled usage |
-| `onConfirm` | `CallbacksOptions['onConfirm']` | Triggered when user confirms time |
-| `onCancel` | `CallbacksOptions['onCancel']` | Triggered when user cancels |
-| `onOpen` | `CallbacksOptions['onOpen']` | Triggered when timepicker opens |
-| `onUpdate` | `CallbacksOptions['onUpdate']` | Triggered during real-time interaction |
-| `onSelectHour` | `CallbacksOptions['onSelectHour']` | Triggered when hour mode is activated |
-| `onSelectMinute` | `CallbacksOptions['onSelectMinute']` | Triggered when minute mode is activated |
-| `onSelectAM` | `CallbacksOptions['onSelectAM']` | Triggered when AM is selected |
-| `onSelectPM` | `CallbacksOptions['onSelectPM']` | Triggered when PM is selected |
-| `onTimezoneChange` | `CallbacksOptions['onTimezoneChange']` | Triggered when timezone changes (plugin) |
-| `onRangeConfirm` | `CallbacksOptions['onRangeConfirm']` | Triggered when range is confirmed (plugin) |
-| `onRangeSwitch` | `CallbacksOptions['onRangeSwitch']` | Triggered when range switches (plugin) |
-| `onRangeValidation` | `CallbacksOptions['onRangeValidation']` | Triggered on range validation (plugin) |
-
-The component extends `React.InputHTMLAttributes`, so all standard input props can be passed directly:
+Full reference: [Props](https://timepicker-ui.vercel.app/react) · [Options](https://timepicker-ui.vercel.app/docs/api/options) · [Events](https://timepicker-ui.vercel.app/docs/api/events) · [TypeScript](https://timepicker-ui.vercel.app/docs/api/typescript)
```tsx
{}}
+ onCancel={(data) => {}}
+ onOpen={(data) => {}}
+ onUpdate={(data) => {}}
+ onSelectHour={(data) => {}}
+ onSelectMinute={(data) => {}}
+ onSelectAM={() => {}}
+ onSelectPM={() => {}}
+ onClear={(data) => {}}
+ onError={(data) => {}}
+ // Plugin callbacks
+ onTimezoneChange={(data) => {}}
+ onRangeConfirm={(data) => {}}
+ onRangeSwitch={(data) => {}}
+ onRangeValidation={(data) => {}}
+ // Any standard prop
className="my-input"
placeholder="Select time"
disabled={false}
- readOnly={false}
- required={true}
- name="time"
id="timepicker-1"
- style={{ width: "200px" }}
- // ... any other input props
/>
```
-### Exported Types
+## Options Overview
-All types are re-exported from `timepicker-ui` core:
+Same options as timepicker-ui core. Full reference: [Options docs](https://timepicker-ui.vercel.app/docs/api/options) · [Configuration guide](https://timepicker-ui.vercel.app/docs/configuration)
```tsx
-import type {
- TimepickerOptions,
- ClockOptions,
- UIOptions,
- LabelsOptions,
- BehaviorOptions,
- CallbacksOptions,
- OptionTypes,
- OpenEventData,
- CancelEventData,
- ConfirmEventData,
- UpdateEventData,
- SelectHourEventData,
- SelectMinuteEventData,
- SelectAMEventData,
- SelectPMEventData,
- ErrorEventData,
- ShowEventData,
- HideEventData,
- SwitchViewEventData,
-} from "timepicker-ui-react";
+
```
-## Configuration Options
+## Themes
-For detailed documentation on all available options, please refer to the [timepicker-ui documentation](https://github.com/pglejzer/timepicker-ui).
+Same 10 themes as core. [Browse all](https://timepicker-ui.vercel.app/docs/features/themes) · [Live examples](https://timepicker-ui.vercel.app/examples/themes/basic)
-### Quick Reference
+Available: `basic`, `crane`, `crane-straight`, `m3-green`, `m2`, `dark`, `glassmorphic`, `pastel`, `ai`, `cyberpunk`
```tsx
-interface OptionTypes {
- // Clock configuration
- clockType?: "12h" | "24h";
- incrementHours?: number;
- incrementMinutes?: number;
- autoSwitchToMinutes?: boolean;
- disabledTime?: {
- hours?: Array;
- minutes?: Array;
- interval?: string | string[];
- };
- currentTime?:
- | boolean
- | {
- time?: Date;
- updateInput?: boolean;
- locales?: string | string[];
- preventClockType?: boolean;
- };
-
- // UI configuration
- theme?:
- | "basic"
- | "crane"
- | "crane-straight"
- | "m2"
- | "m3-green"
- | "dark"
- | "glassmorphic"
- | "pastel"
- | "ai"
- | "cyberpunk";
- animation?: boolean;
- backdrop?: boolean;
- mobile?: boolean;
- enableSwitchIcon?: boolean;
- editable?: boolean;
- enableScrollbar?: boolean;
- cssClass?: string;
- appendModalSelector?: string;
- iconTemplate?: string;
- iconTemplateMobile?: string;
- inline?: {
- enabled: boolean;
- containerId: string;
- showButtons?: boolean;
- autoUpdate?: boolean;
- };
-
- // Labels
- amLabel?: string;
- pmLabel?: string;
- okLabel?: string;
- cancelLabel?: string;
- timeLabel?: string;
- mobileTimeLabel?: string;
- hourMobileLabel?: string;
- minuteMobileLabel?: string;
-
- // Behavior
- focusInputAfterCloseModal?: boolean;
- focusTrap?: boolean;
- delayHandler?: number;
- id?: string;
-
- // Callbacks (use React props instead)
- onOpen?: (data: OpenEventData) => void;
- onCancel?: (data: CancelEventData) => void;
- onConfirm?: (data: ConfirmEventData) => void;
- onUpdate?: (data: UpdateEventData) => void;
- onSelectHour?: (data: SelectHourEventData) => void;
- onSelectMinute?: (data: SelectMinuteEventData) => void;
- onSelectAM?: (data: SelectAMEventData) => void;
- onSelectPM?: (data: SelectPMEventData) => void;
- onError?: (data: ErrorEventData) => void;
-}
-```
+import "timepicker-ui/main.css";
+import "timepicker-ui/theme-dark.css";
-## Architecture
+;
+```
-This package follows strict architectural principles:
+## Plugins
-- **Zero Type Duplication** - All types come directly from `timepicker-ui`
-- **SSR-Safe** - Dynamic imports ensure browser-only code runs client-side
-- **Event Mapping** - React callbacks map to timepicker-ui's EventEmitter API
-- **Composition Over Inheritance** - Clean, maintainable wrapper pattern
-- **Modular Hooks** - Separation of concerns with dedicated hooks for instance, events, value, options, and callbacks
+Docs: [Plugins overview](https://timepicker-ui.vercel.app/docs/features/plugins) · Examples: [Range](https://timepicker-ui.vercel.app/examples/plugins/range) · [Timezone](https://timepicker-ui.vercel.app/examples/plugins/timezone) · [Wheel](https://timepicker-ui.vercel.app/examples/plugins/wheel)
-## Package Structure
+```tsx
+import { PluginRegistry } from "timepicker-ui-react";
+import { RangePlugin } from "timepicker-ui/plugins/range";
+import { TimezonePlugin } from "timepicker-ui/plugins/timezone";
+import { WheelPlugin } from "timepicker-ui/plugins/wheel";
-This repository uses a dual-package structure:
+PluginRegistry.register(RangePlugin);
+PluginRegistry.register(TimezonePlugin);
+PluginRegistry.register(WheelPlugin);
-```
-timepicker-ui-react/
-├── src/ # Library source code
-│ ├── Timepicker/
-│ │ ├── Timepicker.tsx
-│ │ ├── types.ts
-│ │ ├── utils.ts
-│ │ └── hooks/
-│ └── index.ts
-├── docs/ # Demo application (separate package)
-│ ├── package.json # Demo dependencies
-│ ├── vite.config.ts
-│ └── src/
-│ ├── main.tsx
-│ └── App.tsx
-├── package.json # Main library package
-└── tsup.config.ts # Build configuration
+
+ {}} />
+ {}} />
```
-### Main Package
+## SSR / Next.js
-- Production-ready build configuration
-- Only essential dependencies (tsup, typescript, @types/\*)
-- Exports ESM bundle with TypeScript definitions
+```tsx
+"use client";
-### Demo Package
+import { Timepicker } from "timepicker-ui-react";
+import "timepicker-ui/main.css";
-- Separate Vite dev server for live testing
-- Independent dependency management
-- Not published to npm (private: true)
+export default function Page() {
+ return ;
+}
+```
-## Development
+Renders a plain `` on the server, hydrates with the full picker on the client.
-### Build Library
+## Exported Types
-```bash
-npm run build
+All types re-exported from `timepicker-ui`. Full list: [TypeScript docs](https://timepicker-ui.vercel.app/docs/api/typescript)
+
+```tsx
+import type { TimepickerOptions, CallbacksOptions, ConfirmEventData, ... } from "timepicker-ui-react";
+import { TimepickerUI, EventEmitter, PluginRegistry } from "timepicker-ui-react";
```
-### Run Demo
+## Development
```bash
-cd docs
-npm install
-npm run dev
+cd src && npm run build # Build library
+cd src/docs && npm run dev # Run demo
```
-Demo will be available at `http://localhost:3000`
-
-### Project Structure
+## Contributing
-- `src/Timepicker/Timepicker.tsx` - Main component with forwardRef
-- `src/Timepicker/types.ts` - TypeScript interfaces (extends InputHTMLAttributes)
-- `src/Timepicker/utils.ts` - SSR detection helper
-- `src/Timepicker/hooks/useTimepickerInstance.ts` - Dynamic import and instance creation
-- `src/Timepicker/hooks/useEventHandlers.ts` - Event attachment/detachment with callback merging
-- `src/Timepicker/hooks/useTimepickerValue.ts` - Controlled value synchronization
-- `src/Timepicker/hooks/useTimepickerOptions.ts` - Options update handling
-- `src/Timepicker/hooks/useTimepickerCallbacks.ts` - Callback re-attachment on changes
+Contributions welcome! [Open an issue or PR](https://github.com/pglejzer/timepicker-ui-react/issues).
## License
-MIT
-
-## Links
-
-- [timepicker-ui GitHub](https://github.com/pglejzer/timepicker-ui) — Core library repository
-- [timepicker-ui Documentation](https://timepicker-ui.vercel.app) — Full documentation for core library
-- [timepicker-ui-react Documentation](https://timepicker-ui.vercel.app/react) — React wrapper documentation
-- [timepicker-ui-react GitHub](https://github.com/pglejzer/timepicker-ui-react) — This repository
-- [Report Issues](https://github.com/pglejzer/timepicker-ui-react/issues) — Bug reports and feature requests
+MIT © [Piotr Glejzer](https://github.com/pglejzer)
diff --git a/package.json b/package.json
index 1c13ad3..387e4fb 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "timepicker-ui-react",
- "version": "1.2.0",
+ "version": "1.3.0",
"description": "Official React wrapper for timepicker-ui v4.x",
"type": "module",
"sideEffects": false,
@@ -44,6 +44,6 @@
"react-dom": ">=17"
},
"dependencies": {
- "timepicker-ui": "4.2.2"
+ "timepicker-ui": "4.3.0"
}
}
diff --git a/src/index.ts b/src/index.ts
index fcce8ed..cf38cbd 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -37,6 +37,8 @@ export type {
WheelOptions,
WheelScrollStartEventData,
WheelScrollEndEventData,
+ TemplateProvider,
+ ClearHandler,
} from "timepicker-ui";
export { TimepickerUI, EventEmitter, PluginRegistry } from "timepicker-ui";
diff --git a/src/package.json b/src/package.json
index cc2421b..9a34978 100644
--- a/src/package.json
+++ b/src/package.json
@@ -13,7 +13,7 @@
"dependencies": {
"react": "^18.3.1",
"react-dom": "^18.3.1",
- "timepicker-ui": "4.2.2"
+ "timepicker-ui": "4.3.0"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.9.1",