Examples

Todo List

A simple todo list example showcasing VueAirport features.

A simple todo list demonstrating automatic component registration and data synchronization.

0 item(s)
  • Learn Vue Airport
  • Create plugins
  • Contribute
  • Build awesome apps

Project Structure

todo-list/
├── index.ts           # Shared types and injection key
├── TodoList.vue       # Parent component with desk
└── TodoItem.vue       # Child component with auto check-in

Shared Types (index.ts)

import type { InjectionKey } from 'vue';
import type { DeskCore } from '#vue-airport/composables/useCheckIn';

export interface TodoItem {
  label: string;
  done: boolean;
}

export const TODO_DESK_KEY: InjectionKey<DeskCore<TodoItem>> = Symbol('todoDesk');

export { default as TodoList } from './TodoList.vue';
export { default as TodoItem } from './TodoItem.vue';

Parent Component (TodoList.vue)

Creates the desk and manages the todo list state:

<script setup lang="ts">
import { useCheckIn } from '#vue-airport/composables/useCheckIn';
import TodoItem from './TodoItem.vue';
import { type TodoItem as TodoItemData, TODO_DESK_KEY } from '.';

// Create a desk to manage todo items
const { createDesk } = useCheckIn<TodoItemData>();
const { desk } = createDesk(TODO_DESK_KEY, {
  debug: false,
  onCheckIn: (id, data) => {
    console.log(`✅ Item added: ${id}`, data);
  },
  onCheckOut: (id) => {
    console.log(`❌ Item removed: ${id}`);
  },
});

// Local state for managing todos
const todos = ref<Array<{
  id: number;
  label: string;
  done: boolean;
}>>([]);

// Add a new todo item
const addItem = () => {
  const id = Date.now();
  todos.value.push({
    id,
    label: `Task ${id}`,
    done: false,
  });
};

// Toggle the done state
const toggleItem = (id: string | number) => {
  const todo = todos.value.find(t => t.id === id);
  if (todo) {
    todo.done = !todo.done;
  }
};

// Remove a todo item
const removeItem = (id: string | number) => {
  const index = todos.value.findIndex(t => t.id === id);
  if (index !== -1) {
    todos.value.splice(index, 1);
  }
};

// Clear all todos
const clearAll = () => {
  todos.value = [];
  desk.clear();
};
</script>

<template>
  <div>
    <div class="controls">
      <UButton icon="i-heroicons-plus" @click="addItem">
        Add Task
      </UButton>
      <UButton
        color="error"
        variant="soft"
        icon="i-heroicons-trash"
        :disabled="desk.size.value === 0"
        @click="clearAll"
      >
        Clear All
      </UButton>
      <UBadge color="primary" variant="subtle">
        {{ desk.size.value }} item(s)
      </UBadge>
    </div>

    <ul v-if="todos.length > 0" class="item-list">
      <TodoItem
        v-for="todo in todos"
        :key="todo.id"
        :id="todo.id"
        :label="todo.label"
        :done="todo.done"
        @toggle="toggleItem"
        @remove="removeItem"
      />
    </ul>
    <div v-else class="empty-state">
      <p>No tasks. Click "Add Task" to get started.</p>
    </div>
  </div>
</template>

Child Component (TodoItem.vue)

Automatically checks in and synchronizes data:

<script setup lang="ts">
import { useCheckIn } from '#vue-airport/composables/useCheckIn';
import { type TodoItem, TODO_DESK_KEY } from '.';

const props = defineProps<{
  id: string | number;
  label: string;
  done: boolean;
}>();

const emit = defineEmits<{
  toggle: [id: string | number];
  remove: [id: string | number];
}>();

// Auto check-in with data watching enabled
// The component will:
// 1. Check in automatically when mounted
// 2. Watch props changes and update the desk
// 3. Check out automatically when unmounted
useCheckIn<TodoItem>().checkIn(TODO_DESK_KEY, {
  id: props.id,
  autoCheckIn: true,
  watchData: true,
  data: () => ({
    label: props.label,
    done: props.done,
  }),
});
</script>

<template>
  <li class="item">
    <UCheckbox
      :model-value="props.done"
      @update:model-value="emit('toggle', props.id)"
    />
    <span :class="{ done: props.done }">
      {{ props.label }}
    </span>
    <UButton
      size="xs"
      color="error"
      variant="ghost"
      icon="i-heroicons-x-mark"
      @click="emit('remove', props.id)"
    />
  </li>
</template>

Key Concepts

Auto Check-In: Child components automatically register when mounted with autoCheckIn: true.

Watch Data: The desk registry stays synchronized with component props when watchData: true.

Lifecycle Hooks: Parent components can react to check-in/check-out events via onCheckIn and onCheckOut callbacks.

How It Works

  1. Parent creates desk with optional lifecycle hooks
  2. Child components check in automatically when mounted (autoCheckIn: true)
  3. Data synchronizes automatically when props change (watchData: true)
  4. Components check out automatically when unmounted

Usage

<template>
  <TodoList />
</template>