Type Reference
TypeScript types for Announcement, AnnouncementVariant, QueueConfig, AudienceRule, and the full announcements API surface
Complete TypeScript type definitions for the announcements package.
Core Types
AnnouncementConfig
Main configuration object for an announcement:
interface AnnouncementConfig<TMetadata = any> {
// Required
id: string;
variant: AnnouncementVariant;
// Content
title?: string;
description?: string | ReactNode;
media?: AnnouncementMedia;
// Actions
primaryAction?: AnnouncementAction;
secondaryAction?: AnnouncementAction;
// Behavior
priority?: AnnouncementPriority;
frequency?: FrequencyRule;
audience?: AudienceCondition[];
// Variant Options
modalOptions?: ModalOptions;
slideoutOptions?: SlideoutOptions;
bannerOptions?: BannerOptions;
toastOptions?: ToastOptions;
spotlightOptions?: SpotlightOptions;
// Callbacks
onShow?: () => void;
onDismiss?: (reason: DismissalReason) => void;
onComplete?: () => void;
// Custom data
metadata?: TMetadata;
}AnnouncementVariant
type AnnouncementVariant = 'modal' | 'slideout' | 'banner' | 'toast' | 'spotlight';AnnouncementPriority
type AnnouncementPriority = 'low' | 'normal' | 'high' | 'critical';Content Types
AnnouncementMedia
interface AnnouncementMedia {
type: 'image' | 'video' | 'custom';
src?: string;
alt?: string;
poster?: string; // For videos
autoPlay?: boolean; // For videos
component?: ReactNode; // For custom
}Examples:
// Image
media: {
type: 'image',
src: '/feature.png',
alt: 'New feature screenshot',
}
// Video
media: {
type: 'video',
src: '/demo.mp4',
poster: '/demo-poster.jpg',
autoPlay: true,
}
// Custom component
media: {
type: 'custom',
component: <MyCustomMedia />,
}AnnouncementAction
interface AnnouncementAction {
label: string;
onClick: () => void;
href?: string; // Optional link
variant?: 'primary' | 'secondary' | 'ghost';
disabled?: boolean;
}Example:
primaryAction: {
label: 'Try It Now',
onClick: () => router.push('/feature'),
variant: 'primary',
}Frequency Types
FrequencyRule
type FrequencyRule =
| 'once' // Show once ever
| 'session' // Once per session
| 'always' // Every time
| { type: 'times'; count: number } // N times total
| { type: 'interval'; days: number }; // Every N daysExamples:
frequency: 'once'
frequency: 'session'
frequency: { type: 'times', count: 3 }
frequency: { type: 'interval', days: 7 }Audience Types
AudienceCondition
type AudienceCondition<T = any> =
| FieldCondition<T>
| CustomCondition<T>;FieldCondition
interface FieldCondition<T = any> {
field: keyof T;
operator: ComparisonOperator;
value: any;
}
type ComparisonOperator =
| 'equals'
| 'notEquals'
| 'in'
| 'notIn'
| 'greaterThan'
| 'lessThan'
| 'contains'
| 'notContains';Examples:
{ field: 'plan', operator: 'equals', value: 'pro' }
{ field: 'role', operator: 'in', value: ['admin', 'owner'] }
{ field: 'credits', operator: 'greaterThan', value: 100 }CustomCondition
interface CustomCondition<T = any> {
type: 'custom';
check: (context: T) => boolean;
}Example:
{
type: 'custom',
check: (user) => user.isActive && user.hasFeature('analytics'),
}State Types
AnnouncementState
Internal state for each announcement:
interface AnnouncementState {
id: string;
isVisible: boolean;
isActive: boolean;
isDismissed: boolean;
viewCount: number;
lastViewedAt: Date | null;
dismissedAt: Date | null;
dismissalReason: DismissalReason | null;
}DismissalReason
type DismissalReason =
| 'close_button' // User clicked close button
| 'overlay_click' // User clicked overlay
| 'escape_key' // User pressed Escape
| 'primary_action' // User clicked primary action
| 'secondary_action' // User clicked secondary action
| 'auto_dismiss' // Auto-dismissed (toasts)
| 'programmatic'; // Dismissed via codeVariant Options
ModalOptions
interface ModalOptions {
size?: 'sm' | 'md' | 'lg' | 'xl';
closeOnOverlay?: boolean;
closeOnEscape?: boolean;
showCloseButton?: boolean;
centered?: boolean;
}Defaults:
{
size: 'md',
closeOnOverlay: true,
closeOnEscape: true,
showCloseButton: true,
centered: true,
}SlideoutOptions
interface SlideoutOptions {
position?: 'left' | 'right';
size?: 'sm' | 'md' | 'lg';
width?: number;
closeOnOverlay?: boolean;
closeOnEscape?: boolean;
showCloseButton?: boolean;
}Defaults:
{
position: 'right',
size: 'md',
closeOnOverlay: true,
closeOnEscape: true,
showCloseButton: true,
}BannerOptions
interface BannerOptions {
position?: 'top' | 'bottom';
sticky?: boolean;
dismissible?: boolean;
intent?: 'info' | 'success' | 'warning' | 'error';
}Defaults:
{
position: 'top',
sticky: false,
dismissible: true,
intent: 'info',
}ToastOptions
interface ToastOptions {
position?: ToastPosition;
autoDismiss?: boolean;
autoDismissDelay?: number; // milliseconds
showCloseButton?: boolean;
intent?: 'info' | 'success' | 'warning' | 'error';
}
type ToastPosition =
| 'top-left'
| 'top-center'
| 'top-right'
| 'bottom-left'
| 'bottom-center'
| 'bottom-right';Defaults:
{
position: 'bottom-right',
autoDismiss: true,
autoDismissDelay: 5000,
showCloseButton: true,
intent: 'info',
}SpotlightOptions
interface SpotlightOptions {
target: string; // CSS selector
targetElement?: HTMLElement; // Or direct element ref
placement?: Placement;
offset?: number;
padding?: number;
borderRadius?: number;
closeOnClickOutside?: boolean;
closeOnEscape?: boolean;
showCloseButton?: boolean;
}
type Placement =
| 'top'
| 'top-start'
| 'top-end'
| 'bottom'
| 'bottom-start'
| 'bottom-end'
| 'left'
| 'left-start'
| 'left-end'
| 'right'
| 'right-start'
| 'right-end';Defaults:
{
placement: 'bottom',
offset: 8,
padding: 8,
borderRadius: 4,
closeOnClickOutside: true,
closeOnEscape: true,
showCloseButton: true,
}Provider Types
AnnouncementsProviderProps
interface AnnouncementsProviderProps<TUserContext = any> {
// Required
announcements: AnnouncementConfig[];
children: ReactNode;
// Optional
userContext?: TUserContext;
queueConfig?: QueueConfig;
storage?: StorageType;
storageKey?: string;
// Callbacks
onShow?: (id: string) => void;
onDismiss?: (id: string, reason: DismissalReason) => void;
onComplete?: (id: string) => void;
onQueueChange?: (queue: AnnouncementConfig[]) => void;
// Debug
debug?: boolean;
}QueueConfig
interface QueueConfig {
maxConcurrent?: number; // Max announcements shown at once
autoShow?: boolean; // Auto-show next in queue
delayBetween?: number; // Delay between announcements (ms)
respectVariants?: boolean; // Allow one per variant
}Defaults:
{
maxConcurrent: 1,
autoShow: true,
delayBetween: 0,
respectVariants: false,
}StorageType
type StorageType = 'localStorage' | 'sessionStorage' | 'memory';Hook Return Types
UseAnnouncementReturn
interface UseAnnouncementReturn {
// State
state: AnnouncementState;
config: AnnouncementConfig;
isVisible: boolean;
isActive: boolean;
isDismissed: boolean;
canShow: boolean;
viewCount: number;
// Actions
show: () => void;
hide: () => void;
dismiss: (reason?: DismissalReason) => void;
complete: () => void;
reset: () => void;
}UseAnnouncementsReturn
interface UseAnnouncementsReturn {
// All announcements
announcements: {
all: AnnouncementState[];
visible: AnnouncementState[];
queued: AnnouncementState[];
dismissed: AnnouncementState[];
};
// Actions
showNext: () => void;
hideAll: () => void;
resetAll: () => void;
}UseAnnouncementQueueReturn
interface UseAnnouncementQueueReturn {
// Queue state
queue: AnnouncementConfig[];
queueSize: number;
activeCount: number;
// Actions
showNext: () => void;
clearQueue: () => void;
}Component Props
AnnouncementModal Props
interface AnnouncementModalProps {
id?: string; // Announcement ID
config?: AnnouncementConfig; // Or inline config
onDismiss?: (reason: DismissalReason) => void;
}AnnouncementSlideout Props
interface AnnouncementSlideoutProps {
id?: string;
config?: AnnouncementConfig;
onDismiss?: (reason: DismissalReason) => void;
}AnnouncementBanner Props
interface AnnouncementBannerProps {
id?: string;
config?: AnnouncementConfig;
onDismiss?: (reason: DismissalReason) => void;
}AnnouncementToast Props
interface AnnouncementToastProps {
id?: string;
config?: AnnouncementConfig;
onDismiss?: (reason: DismissalReason) => void;
}AnnouncementSpotlight Props
interface AnnouncementSpotlightProps {
id?: string;
config?: AnnouncementConfig;
onDismiss?: (reason: DismissalReason) => void;
}Headless Component Props
HeadlessModal Props
interface HeadlessModalProps {
id?: string;
config?: AnnouncementConfig;
children: (props: HeadlessModalRenderProps) => ReactNode;
}
interface HeadlessModalRenderProps {
state: AnnouncementState;
config: AnnouncementConfig;
isOpen: boolean;
dismiss: (reason: DismissalReason) => void;
complete: () => void;
}HeadlessSlideout Props
interface HeadlessSlideoutProps {
id?: string;
config?: AnnouncementConfig;
children: (props: HeadlessSlideoutRenderProps) => ReactNode;
}
interface HeadlessSlideoutRenderProps {
state: AnnouncementState;
config: AnnouncementConfig;
isOpen: boolean;
dismiss: (reason: DismissalReason) => void;
complete: () => void;
}HeadlessBanner Props
interface HeadlessBannerProps {
id?: string;
config?: AnnouncementConfig;
children: (props: HeadlessBannerRenderProps) => ReactNode;
}
interface HeadlessBannerRenderProps {
state: AnnouncementState;
config: AnnouncementConfig;
isVisible: boolean;
dismiss: (reason: DismissalReason) => void;
complete: () => void;
}HeadlessToast Props
interface HeadlessToastProps {
id?: string;
config?: AnnouncementConfig;
children: (props: HeadlessToastRenderProps) => ReactNode;
}
interface HeadlessToastRenderProps {
state: AnnouncementState;
config: AnnouncementConfig;
isVisible: boolean;
dismiss: (reason: DismissalReason) => void;
complete: () => void;
progress: number; // 0-100 for auto-dismiss
}HeadlessSpotlight Props
interface HeadlessSpotlightProps {
id?: string;
config?: AnnouncementConfig;
children: (props: HeadlessSpotlightRenderProps) => ReactNode;
}
interface HeadlessSpotlightRenderProps {
state: AnnouncementState;
config: AnnouncementConfig;
isVisible: boolean;
targetRect: DOMRect | null;
dismiss: (reason: DismissalReason) => void;
complete: () => void;
}Utility Types
Generic Metadata
Extend announcement configs with custom metadata:
interface CustomMetadata {
campaignId: string;
source: 'email' | 'in-app' | 'push';
experimentId?: string;
}
const config: AnnouncementConfig<CustomMetadata> = {
id: 'promo',
variant: 'modal',
title: 'Special Offer',
metadata: {
campaignId: 'summer-2024',
source: 'in-app',
},
};Generic User Context
Type-safe user context:
interface UserContext {
plan: 'free' | 'pro' | 'enterprise';
role: 'user' | 'admin';
features: string[];
}
<AnnouncementsProvider<UserContext>
userContext={{
plan: 'pro',
role: 'user',
features: ['analytics'],
}}
announcements={announcements}
/>Import Paths
// Types
import type {
AnnouncementConfig,
AnnouncementVariant,
AnnouncementPriority,
FrequencyRule,
AudienceCondition,
DismissalReason,
QueueConfig,
UseAnnouncementReturn,
UseAnnouncementsReturn,
UseAnnouncementQueueReturn,
} from '@tour-kit/announcements';
// Components
import {
AnnouncementsProvider,
AnnouncementModal,
AnnouncementSlideout,
AnnouncementBanner,
AnnouncementToast,
AnnouncementSpotlight,
} from '@tour-kit/announcements';
// Headless
import {
HeadlessModal,
HeadlessSlideout,
HeadlessBanner,
HeadlessToast,
HeadlessSpotlight,
} from '@tour-kit/announcements/headless';
// Hooks
import {
useAnnouncement,
useAnnouncements,
useAnnouncementQueue,
} from '@tour-kit/announcements';Component Composition Props
Compound parts used to build the styled variants. Most consumers use the variant components directly (Modal, Slideout, Banner, Toast, Spotlight); these props matter when you compose your own layout.
AnnouncementOverlayProps
interface AnnouncementOverlayProps
extends React.HTMLAttributes<HTMLDivElement>,
VariantProps<typeof overlayVariants> {
/** Whether the overlay is visible */
open?: boolean
/** Callback when overlay is clicked */
onClose?: () => void
}AnnouncementContentProps
interface AnnouncementContentProps extends React.HTMLAttributes<HTMLDivElement> {
title?: string
description?: React.ReactNode
media?: AnnouncementMedia
titleProps?: React.HTMLAttributes<HTMLHeadingElement>
descriptionProps?: React.HTMLAttributes<HTMLDivElement>
}AnnouncementActionsProps
interface AnnouncementActionsProps extends React.HTMLAttributes<HTMLDivElement> {
primaryAction?: AnnouncementAction
secondaryAction?: AnnouncementAction
onAction?: (type: 'primary' | 'secondary') => void
/** Fired when action.dismissOnClick is true */
onDismiss?: () => void
direction?: 'horizontal' | 'vertical'
}AnnouncementCloseProps
interface AnnouncementCloseProps
extends Omit<React.ComponentPropsWithoutRef<'button'>, 'children'> {
asChild?: boolean
children?: React.ReactNode | RenderProp
onClose?: () => void
}Context Types
AnnouncementsContext
Raw React context. Prefer useAnnouncementsContext — it throws when used outside <AnnouncementsProvider>.
declare const AnnouncementsContext: React.Context<AnnouncementsContextValue | null>AnnouncementsContextValue
interface AnnouncementsContextValue {
announcements: Map<string, AnnouncementState>
activeAnnouncement: string | null
queue: string[]
queueConfig: QueueConfig
register: (config: AnnouncementConfig) => void
unregister: (id: string) => void
show: (id: string) => void
hide: (id: string) => void
dismiss: (id: string, reason?: DismissalReason) => void
complete: (id: string) => void
reset: (id: string) => void
resetAll: () => void
getState: (id: string) => AnnouncementState | undefined
getConfig: (id: string) => AnnouncementConfig | undefined
canShow: (id: string) => boolean
showNext: () => void
clearQueue: () => void
}Queue Types
QueueItem
interface QueueItem {
id: string
priority: AnnouncementPriority
addedAt: number
weight: number
/** Monotonic sequence number for ordering items with same timestamp */
sequence: number
}DEFAULT_QUEUE_CONFIG
declare const DEFAULT_QUEUE_CONFIG: QueueConfigThe default queue configuration the provider falls back to when no queueConfig is supplied. Use as a base when building a partial override.
AnnouncementScheduler
Class that manages the queue and gating decisions internally. Exported for advanced use (custom orchestrators, test fixtures); typical apps never construct it directly.
declare class AnnouncementScheduler {
constructor(config: QueueConfig)
canShow(config, state, userContext?): boolean
shouldQueue(config, state, userContext?): boolean
enqueue(config: AnnouncementConfig): number
getNext(): string | undefined
peekNext(): string | undefined
remove(id: string): boolean
isQueued(id: string): boolean
getQueuePosition(id: string): number
getQueuedIds(): string[]
get queueSize(): number
canShowMore(): boolean
markActive(): void
markInactive(): void
get currentActiveCount(): number
clearQueue(): void
resetActive(): void
updateConfig(config: QueueConfig): void
get delayBetween(): number
get autoShow(): boolean
}Storage
AnnouncementStorageAdapter
Pluggable persistence layer. Default storage uses localStorage; pass an adapter on the provider to swap implementations (memory, cookies, SQLite, ...).
interface AnnouncementStorageAdapter {
getItem(key: string): string | null | Promise<string | null>
setItem(key: string, value: string): void | Promise<void>
removeItem(key: string): void | Promise<void>
}Analytics Event Types
The provider's onEvent callback receives an AnnouncementEvent, which is a discriminated union of seven concrete shapes.
AnnouncementEventType
type AnnouncementEventType =
| 'announcement_registered'
| 'announcement_shown'
| 'announcement_dismissed'
| 'announcement_completed'
| 'announcement_action_clicked'
| 'announcement_queued'
| 'announcement_dequeued'BaseAnnouncementEvent
interface BaseAnnouncementEvent {
type: AnnouncementEventType
announcementId: string
variant: AnnouncementVariant
timestamp: number
metadata?: Record<string, unknown>
}Concrete events
interface AnnouncementRegisteredEvent extends BaseAnnouncementEvent {
type: 'announcement_registered'
}
interface AnnouncementShownEvent extends BaseAnnouncementEvent {
type: 'announcement_shown'
viewCount: number
fromQueue: boolean
}
interface AnnouncementDismissedEvent extends BaseAnnouncementEvent {
type: 'announcement_dismissed'
reason: DismissalReason
viewDuration: number
}
interface AnnouncementCompletedEvent extends BaseAnnouncementEvent {
type: 'announcement_completed'
viewDuration: number
}
interface AnnouncementActionClickedEvent extends BaseAnnouncementEvent {
type: 'announcement_action_clicked'
actionType: 'primary' | 'secondary'
actionLabel: string
}
interface AnnouncementQueuedEvent extends BaseAnnouncementEvent {
type: 'announcement_queued'
queuePosition: number
}
interface AnnouncementDequeuedEvent extends BaseAnnouncementEvent {
type: 'announcement_dequeued'
reason: 'shown' | 'expired' | 'removed'
}
type AnnouncementEvent =
| AnnouncementRegisteredEvent
| AnnouncementShownEvent
| AnnouncementDismissedEvent
| AnnouncementCompletedEvent
| AnnouncementActionClickedEvent
| AnnouncementQueuedEvent
| AnnouncementDequeuedEventDiscriminate on event.type — TypeScript narrows the union to the per-event payload.
Cross-Cutting
| Type | Notes |
|---|---|
UILibrary | 'radix-ui' | 'base-ui' — see Unified Slot |
RenderProp | Generic render-prop callback shape — see Unified Slot |
Related
- AnnouncementsProvider - Provider configuration
- Components - Styled components
- Hooks - Hook APIs
- Headless - Headless components
- useAnnouncementsContext - Raw context hook