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:

KeyTypeDescription
'currentUser'TUser | nullLogged-in user object
'currentOrganization'any | nullCurrent tenant/org loaded by middleware
'tenant'{ slug: string } | nullSubdomain slug (customer app)
'tenantNotFound'booleanWhether 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: '',
})

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 stateCreate a separate store file
Use reactive for form objectsUse ref for objects you need to destructure
Use useCookie for tokensStore tokens in localStorage
Clear state on logout (null assignments)Leave stale state after session end
Co-locate state with the composable that manages itScatter useState calls across random components