Frontend
State Management
How reactive state is managed across the Booki frontend apps.
There is no external state management library (no Pinia, no Vuex). State is managed using Vue's built-in reactivity APIs and Nuxt's useState.
Global State — useState
Use useState when state needs to be shared across multiple components or pages without prop drilling. The key string is the unique identifier:
// Set / initialise (typically in a composable or middleware)
const currentUser = useState<TUser | null>('currentUser', () => null)
// Read anywhere — same key returns the same reactive ref
const currentUser = useState<TUser | null>('currentUser')
Established global state keys:
| Key | Type | Description |
|---|---|---|
'currentUser' | TUser | null | Logged-in user object |
'currentOrganization' | any | null | Current tenant/org loaded by middleware |
'tenant' | { slug: string } | null | Subdomain slug (customer app) |
'tenantNotFound' | boolean | Whether the tenant org exists |
Local State — ref and reactive
Use ref for primitives and single values, reactive for objects:
// Primitives
const isLoading = ref(false)
const searchQuery = ref('')
const selectedId = ref<string | null>(null)
// Form objects — use reactive so properties are individually bindable
const form = reactive({
firstName: '',
lastName: '',
email: '',
phone: '',
})
const errors = reactive({
firstName: '',
email: '',
})
Cookie State
Authentication tokens are stored in cookies managed by useCookie. The cookie config (expiry, secure, sameSite) comes from runtimeConfig.public.cookieConfig:
const token = useCookie<string | null>('accessToken', cookieConfig)
// Set
token.value = response.accessToken
// Clear
token.value = null
Dialog / Modal State
Modal visibility is kept as a reactive object per page — one object, multiple boolean flags:
const dialogs = reactive({
view: false,
cancel: false,
reschedule: false,
})
// Open
dialogs.view = true
// Close (from child emit)
function onClose() {
dialogs.view = false
selected.value = null
}
Do's and Don'ts
| ✅ Do | ❌ Don't |
|---|---|
Use useState('key') for cross-component state | Create a separate store file |
Use reactive for form objects | Use ref for objects you need to destructure |
Use useCookie for tokens | Store tokens in localStorage |
Clear state on logout (null assignments) | Leave stale state after session end |
| Co-locate state with the composable that manages it | Scatter useState calls across random components |