Getting Started

API Reference

Complete API documentation for useCheckIn composable

Complete API reference for the useCheckIn composable and related types.

useCheckIn()

The main composable for creating and managing check-in desks.

const { createDesk, checkIn, standaloneDesk, generateId, memoizedId, isCheckedIn, getRegistry, clearIdCache } = useCheckIn<T, TContext>();

Type Parameters

ParameterDescriptionDefault
TType of data stored in the deskany
TContextType of additional context provided by the desk{}

Returned Methods

createDesk()

Creates a check-in desk and provides it to child components.

createDesk(
  injectionKey: InjectionKey<DeskWithContext<T, TContext>>,
  options?: DeskCoreOptions<T> & { context?: TContext }
): { desk: DeskWithContext<T, TContext>, injectionKey: InjectionKey }

Parameters:

NameTypeDescription
injectionKeyInjectionKey<DeskWithContext<T, TContext>>Symbol-based injection key for dependency injection
optionsDeskCoreOptions<T> & { context?: TContext }Desk configuration and context

Options:

OptionTypeDescriptionDefault
contextTContextAdditional context to share with child componentsundefined
onBeforeCheckIn(id, data) => boolean | undefinedHook called before check-in, return false to cancelundefined
onCheckIn(id, data) => voidHook called after successful check-inundefined
onBeforeCheckOut(id) => boolean | undefinedHook called before check-out, return false to cancelundefined
onCheckOut(id) => voidHook called after successful check-outundefined
debugbooleanEnable debug loggingfalse
devToolsbooleanEnable Vue DevTools integrationfalse
pluginsCheckInPlugin<T>[]Array of plugins to extend functionality[]
deskIdstringIdentifier for DevTools displayAuto-generated

Returns: An object containing:

  • desk: The desk instance with context
  • injectionKey: The injection key used

Example:

const TABS_DESK_KEY = Symbol('tabsDesk');

const { createDesk } = useCheckIn<TabItemData, TabItemContext>();
const { desk } = createDesk(TABS_DESK_KEY, {
  devTools: true,
  context: {
    activeTab: ref('tab-1'),
    selectTab: (id) => { /* ... */ },
  },
  onCheckIn: (id, data) => {
    console.log('Tab checked in:', id, data);
  },
});

checkIn()

Registers a child component with a desk.

checkIn(
  parentDeskOrKey: DeskCore<T> & TContext | InjectionKey<DeskCore<T> & TContext> | null | undefined,
  checkInOptions?: CheckInOptions<T, TContext>
): CheckInResult<T, TContext>

Parameters:

NameTypeDescription
parentDeskOrKeyDeskCore<T> & TContext | InjectionKey | null | undefinedDesk instance or injection key
checkInOptionsCheckInOptions<T, TContext>Check-in configuration

Check-in Options:

OptionTypeDescriptionDefault
idstring | numberUnique identifier for this componentAuto-generated
autoCheckInbooleanAutomatically check in on component mountfalse
dataT | (() => T) | (() => Promise<T>) | ((desk) => T) | ((desk) => Promise<T>)Data to register, or sync/async function that returns data. When using a function, it receives the desk instance as a parameter, allowing access to desk context without manual inject callsundefined
watchDatabooleanWatch data for changes and auto-update the desk registry. Works with both sync and async datafalse
shallowbooleanUse shallow watching (deep: false) instead of deep watching when watchData is enabled. Set to true for performance optimization when only tracking object reference changes, or false to detect nested property changesfalse
watchCondition(() => boolean) | Ref<boolean>Conditional check-in/check-out based on reactive state. The component will automatically check in when the condition becomes true and check out when it becomes false. Works independently of autoCheckIn, which only controls the initial check-in behaviorundefined
metaRecord<string, any>Additional metadata visible in DevTools inspectorundefined
debugbooleanEnable debug logging for this check-infalse

Returns: An object containing:

  • desk: Reference to the desk (with context if provided)
  • checkOut(): Function to manually check out
  • updateSelf(newData?): Function to manually update data

Example:

const { checkIn } = useCheckIn<TabItemData, TabItemContext>();

// Example 1: Basic auto check-in with data watching
const { desk, checkOut, updateSelf } = checkIn(TABS_DESK_KEY, {
  id: props.id,
  autoCheckIn: true,
  watchData: true,
  data: (desk) => ({
    label: desk.tabsData.value.find(t => t.id === props.id)?.label ?? '',
    content: desk.tabsData.value.find(t => t.id === props.id)?.content ?? '',
  }),
});

// Example 2: Conditional check-in based on visibility
const isVisible = ref(true);
checkIn(MY_DESK_KEY, {
  id: 'item-1',
  watchCondition: isVisible,
  data: { name: 'My Item' },
});

// Example 3: Shallow watching for performance
const largeList = ref([{ id: 1 }, { id: 2 }, { id: 3 }]);
checkIn(LIST_DESK_KEY, {
  id: 'list-1',
  autoCheckIn: true,
  watchData: true,
  shallow: true,
  data: () => largeList.value,
});

// Example 4: Async data with race condition protection
checkIn(USER_DESK_KEY, {
  id: props.userId,
  autoCheckIn: true,
  watchData: true,
  data: async (desk) => {
    const response = await fetch(`/api/users/${props.userId}`);
    return await response.json();
  },
});
Key Features:
  • Desk Parameter: The data function receives the desk instance, eliminating the need for manual inject calls
  • Async Data: Both sync and async data functions are supported with built-in race condition protection
  • Conditional Check-in: watchCondition works independently of autoCheckIn. When autoCheckIn: true, it controls the initial check-in only if the condition is true. The watcher remains active regardless
  • Performance: Use shallow: true for large objects when you only need to track reference changes

watchCondition Behavior

The watchCondition option enables dynamic check-in/check-out based on reactive state:

// Scenario 1: autoCheckIn: false (default)
const isActive = ref(false);
checkIn(MY_DESK_KEY, {
  watchCondition: isActive,
  autoCheckIn: false,
});
// No initial check-in (even if isActive is true)
// Waits for isActive to become true for first check-in
// Automatically checks out when isActive becomes false

// Scenario 2: autoCheckIn: true
const isActive = ref(true);
checkIn(MY_DESK_KEY, {
  watchCondition: isActive,
  autoCheckIn: true,
});
// Immediate check-in if isActive is already true
// Watch remains active for future condition changes
// Automatically checks out when isActive becomes false

// Scenario 3: Complex conditions
checkIn(MY_DESK_KEY, {
  watchCondition: () => isLoggedIn.value && hasPermission.value && !isDisabled.value,
  autoCheckIn: true,
});
// Checks in/out based on combined conditions

standaloneDesk()

Creates a standalone desk without dependency injection (useful for testing).

standaloneDesk(options?: DeskCoreOptions<T>): DeskCore<T>

Example:

const desk = standaloneDesk<TabItemData>({
  debug: true,
});

desk.checkIn('tab-1', { label: 'Home' });

generateId()

Generates a cryptographically secure unique ID.

generateId(prefix?: string): string

Parameters:

  • prefix: Optional prefix for the ID (default: 'item')

Example:

const id = generateId('tab');

memoizedId()

Generates a memoized ID for a component, ensuring stability across remounts.

memoizedId(
  instanceOrId: object | string | number | null | undefined,
  prefix?: string
): string

Parameters:

  • instanceOrId: Vue instance, custom ID, or null
  • prefix: Optional prefix for the ID

Example:

import { getCurrentInstance } from 'vue';

const instance = getCurrentInstance();
const id = memoizedId(instance, 'tab');

isCheckedIn()

Computed helper to check if a specific ID is checked in.

isCheckedIn<T, TContext>(
  desk: DeskCore<T> & TContext,
  id: string | number | Ref<string | number>
): ComputedRef<boolean>

Example:

const isTabActive = isCheckedIn(desk, 'tab-1');

getRegistry()

Computed helper to get the registry as an array, with optional sorting.

getRegistry<T, TContext>(
  desk: DeskCore<T> & TContext,
  options?: { sortBy?: keyof T | 'timestamp'; order?: 'asc' | 'desc' }
): ComputedRef<CheckInItem<T>[]>

Example:

// Get all tabs sorted by timestamp
const allTabs = getRegistry(desk, {
  sortBy: 'timestamp',
  order: 'desc'
});

clearIdCache()

Clears the memoization cache for custom IDs.

clearIdCache(resetCounter?: boolean): void

Parameters:

  • resetCounter: Whether to reset the instance counter (default: false)

DeskCore API

The desk instance provides methods to interact with the registry.

Methods

checkIn()

Manually register an item.

desk.checkIn(id: string | number, data: T, meta?: Record<string, any>): boolean

Returns: true if successful, false if cancelled by plugin/hook

checkOut()

Manually unregister an item.

desk.checkOut(id: string | number): boolean

Returns: true if successful, false if not found or cancelled

update()

Update an existing item's data.

desk.update(id: string | number, data: T): boolean

Returns: true if successful, false if not found

get()

Retrieve a specific item by ID.

desk.get(id: string | number): CheckInItem<T> | undefined

has()

Check if an ID exists in the registry.

desk.has(id: string | number): boolean

getAll()

Get all items, optionally sorted.

desk.getAll(options?: {
  sortBy?: keyof T | 'timestamp';
  order?: 'asc' | 'desc';
}): CheckInItem<T>[]

count()

Get the number of checked-in items.

desk.count(): number

Returns: Reactive computed ref with the count

clear()

Remove all items from the registry.

desk.clear(): void

on()

Subscribe to desk events.

desk.on(event: DeskEventType, callback: DeskEventCallback<T>): () => void

Events:

  • 'check-in': Fired when an item checks in
  • 'check-out': Fired when an item checks out
  • 'update': Fired when an item is updated
  • 'clear': Fired when the desk is cleared

Returns: Unsubscribe function

Example:

const unsubscribe = desk.on('check-in', ({ id, data }) => {
  console.log(`Item ${id} checked in:`, data);
});

// Later: unsubscribe()

off()

Unsubscribe from desk events.

desk.off(event: DeskEventType, callback: DeskEventCallback<T>): void

destroy()

Clean up the desk and remove all listeners.

desk.destroy(): void
Memory Management:desk automatically cleans up on component unmount. Use destroy() for manual cleanup in long-running applications.

Types

CheckInItem

Represents an item in the desk registry.

interface CheckInItem<T = any> {
  id: string | number;
  data: T;
  timestamp?: number;
  meta?: Record<string, any>;
}

DeskEventType

Type of events emitted by the desk.

type DeskEventType = 'check-in' | 'check-out' | 'update' | 'clear';

DeskEventCallback

Callback signature for desk events.

type DeskEventCallback<T = any> = (payload: {
  id?: string | number;
  data?: T;
  timestamp: number;
}) => void;

CheckInPlugin

Plugin interface for extending desk functionality.

interface CheckInPlugin<T = any> {
  name: string;
  onBeforeCheckIn?: (id: string | number, data: T, desk: DeskCore<T>) => boolean | undefined;
  onCheckIn?: (id: string | number, data: T, desk: DeskCore<T>) => void;
  onBeforeCheckOut?: (id: string | number, desk: DeskCore<T>) => boolean | undefined;
  onCheckOut?: (id: string | number, desk: DeskCore<T>) => void;
  onUpdate?: (id: string | number, data: T, desk: DeskCore<T>) => void;
}

Best Practices

Type Safety

Always provide type parameters for useCheckIn<T, TContext>() to get full type safety and IntelliSense support:

interface MyData {
  name: string;
  value: number;
}

interface MyContext {
  selectedId: Ref<string>;
  selectItem: (id: string) => void;
}

const { createDesk, checkIn } = useCheckIn<MyData, MyContext>();

Desk Parameter in Data Function

When using the desk parameter in the data function:

// Good: Lightweight computation
data: (desk) => ({
  label: desk.items.value.find(i => i.id === props.id)?.label ?? 'Unknown'
})

// Caution: Heavy computation with watchData
data: (desk) => {
  // This runs on every data change when watchData: true
  return expensiveCalculation(desk.data.value);
}

Important: When watchData: true, the function is re-evaluated on each change. Keep the logic lightweight to avoid performance issues.

Shallow vs Deep Watching

Choose the appropriate watching strategy based on your data structure:

// Use shallow: true for large arrays/objects when only tracking replacements
const items = ref([{ id: 1 }, { id: 2 }, ...largeList]);
checkIn(DESK_KEY, {
  watchData: true,
  shallow: true, // Only detects items.value = newArray
  data: () => items.value,
});

// Use shallow: false (default) for nested property changes
const formData = ref({ user: { name: '', email: '' } });
checkIn(FORM_DESK_KEY, {
  watchData: true,
  shallow: false, // Detects formData.value.user.name = 'John'
  data: () => formData.value,
});

Conditional Check-in Patterns

Use watchCondition for dynamic component participation:

// Tab visibility
checkIn(TABS_DESK_KEY, {
  id: props.tabId,
  watchCondition: () => props.visible,
  data: { label: props.label },
});

// User permissions
checkIn(ADMIN_DESK_KEY, {
  id: props.userId,
  watchCondition: () => hasAdminRole.value,
  data: () => userData.value,
});

// Combined conditions
checkIn(DESK_KEY, {
  id: props.id,
  watchCondition: () => isEnabled.value && !isLoading.value && hasData.value,
  autoCheckIn: true, // Initial check-in if all conditions are met
  data: myData,
});

Auto Check-in vs Manual

Choose the appropriate registration strategy:

// Auto check-in: Component should always be registered
checkIn(DESK_KEY, {
  autoCheckIn: true,
  data: myData,
});

// Manual check-in: Need control over timing
const { desk, checkOut } = checkIn(DESK_KEY, {
  autoCheckIn: false,
  data: myData,
});

// Later, when ready...
if (someCondition) {
  // Manual check-in via updateSelf or direct desk.checkIn
}

// Conditional check-in: Dynamic participation
checkIn(DESK_KEY, {
  watchCondition: isActive,
  data: myData,
});

Async Data Handling

VueAirport includes built-in protection against race conditions for async data:

// Safe: Automatic race condition protection
checkIn(USER_DESK_KEY, {
  id: props.userId,
  autoCheckIn: true,
  watchData: true,
  data: async (desk) => {
    // Slow API call
    const response = await fetch(`/api/users/${props.userId}`);
    const userData = await response.json();
    
    // If props.userId changed during the fetch, this update is automatically discarded
    return userData;
  },
});

The async update guard ensures that only the most recent update is applied, preventing stale data from overwriting newer data.

Memory Management

The desk automatically cleans up on component unmount. For manual cleanup in long-running applications:

// Clear memoized ID cache
clearIdCache(true); // true = reset counter

// Destroy a desk manually
desk.destroy();

// Clear all items from a desk
desk.clear();

DevTools Integration

Add meaningful metadata for better debugging experience:

checkIn(DESK_KEY, {
  id: props.id,
  autoCheckIn: true,
  data: myData,
  meta: {
    label: 'User Profile Form',
    componentType: 'form',
    priority: 'high',
    createdBy: 'AuthModule',
  },
});

Plugin Usage

Extend desk functionality with plugins:

const validationPlugin = {
  name: 'validation',
  onBeforeCheckIn: (id, data, desk) => {
    if (!data.email) {
      console.warn('Email is required');
      return false; // Cancel check-in
    }
    return true;
  },
};

createDesk(FORM_DESK_KEY, {
  plugins: [validationPlugin],
  devTools: true,
});