Getting Started

Concept

Lessons learned from building Nuxt Directus, the concept.

Introduction

Nuxt Directus is a module for Nuxt that simplifies the integration of Directus into your Nuxt application. It provides a set of components and helpers to make it easier to build your application.

The v5.0 version was based on the concept of writing functions to call each API endpoint itself, and "rebuilding" the SDK, as other modules like nuxt-strapi do.

This concept has brought some problems with it, among other things you had to define the types yourself, and of course also maintain and adapt each individual type as soon as Directus changes something, which turns out to be difficult.

In addition, many functions are missing, because building an entire SDK is not exactly easy.

Directus released a better and new SDK, which led us to integrate this SDK with its good new TypeScript support and many other features into nuxt-directus.

This means that we no longer have to maintain the types ourselves, and we can use the SDK directly

New Concept

Directus Clients

If you ever saw any Directus SDK example you are aware that you have to create your own clients before being able to do any operation. With nuxt-directus, on the other hand, each of the main composables comes with its own client, battery included! 🚀

This means that to read a collection's data, you just have to do this:

-import { createDirectus, rest, readItems } from '@directus/sdk'
-const client = createDirectus('directus_project_url').with(rest())
-const result = await client.request(readItems('collection_name'))

+const { readItems } = useDirectusItems()
+const items = await readItems('collection_name')

SSR support

By default all read* functions (with the exception of readMe) have a readAsync* alternative, taking advantage of Nuxt's useAsyncData. This means that if Nuxt aleady fetched the content from Directus server side, the client wont fetch it a second time during hydration. You can customize the key param of all readAsync* functions and match the behaviour of useFetch:

const { data, error } = await readAsyncItems('posts', {
  key: 'posts'
})
// once fetched it also becomes accessible via
const { data } = useNuxtData('posts')

Naming Conventions

All functions follow the Directus SDK naming conventions, and each one will be available under their related composable that follows standard Nuxt/Vue naming conventions.

const {
  client,
  acceptUserInvite,
  inviteUser,
  login,
  logout,
  passwordRequest,
  passwordReset,
  refresh,
  refreshTokenCookie,
  readMe,
  setUser,
  setTokens,
  tokens,
  user
} = useDirectusAuth()

Static Tokens

By default, any static token provided via staticToken will be used, while prioritizing user authentication. This can be bypassed by using the useStaticToken property available in all main composables, as well give you the ability to pass your own tokens.

// only using the static token in the runtimeConfig
const { readSingleton } = useDirectusItems<MySchema>({ staticToken: true })
const globals = await readSingleton('globals')

// using a custom static token
const client = useDirectusRest({ staticToken: 'my-super-long-token' })

// bypassing the static token, only using Public or the currently authenticated user's permissions
const { readItems } = useDirectusItems<MySchema>({ staticToken: false })
const posts = await readItems('posts')

This is also true for both other clients: useDirectusGraphql, useDirectusRealtime.

TypeScript Support

Type support is provided to each main composable, making sure to instruct its built-in client and provide full type support to each function.

import type { MySchema, Post } from '~/types'

const { createItem, readItems, updateItem } = useDirectusItems<MySchema>()

const { data: posts, refresh: refreshPosts } = await useAsyncData(() => readItems('posts', {
  fields: ['title', 'id', 'slug', 'content'],
  nuxtData: false
}))

const newPostData = ref<Partial<Post>>({})

async function createContent () {
  await createItem('posts', newPostData.value)
  refreshPosts()
}

const postId = ref<Post['id']>(posts.value?.[0].id || '')
const updatePostData = ref<Partial<Post>>({})

async function updateContent () {
  await updateItem('posts', postId.value, updatePostData.value)
  refreshPosts()
}

Copyright © 2026. All rights reserved.