N
Nuxt3mo ago
Dark_Soul

Correct way to create a compostable

Hello, I would like to know what is the correct way to create a composable, I am very confused because I do not know in which case to use function or constant, in which case to implement the await to render first in the dom of the page or component.....
5 Replies
kapa.ai
kapa.ai3mo ago
Hi @Dark_Soul. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Creating Composables in Nuxt In Nuxt, composables are a powerful way to reuse logic across your application. Let me explain the correct approaches to creating them. Function vs Constant Export You can create composables using either method: Method 1: Using named export with a constant
export const useFoo = () => {
return useState('foo', () => 'bar')
}
export const useFoo = () => {
return useState('foo', () => 'bar')
}
Method 2: Using default export with a function
// Will be available as useFoo() (camelCase of file name without extension)
export default function () {
return useState('foo', () => 'bar')
}
// Will be available as useFoo() (camelCase of file name without extension)
export default function () {
return useState('foo', () => 'bar')
}
Both approaches work, but the named export (Method 1) is more explicit and commonly used in the Nuxt ecosystem Nuxt Directory Structure: Composables. Async Composables and await When creating composables that use async operations like useFetch or useAsyncData, there's an important rule: Do not use await inside the composable definition itself. Instead, let the caller decide whether to await the result.
// CORRECT: Return the promise for the caller to await
export function useCustomFetch(url, options = {}) {
// Set up your custom options
const defaults = { /* your defaults */ }
const params = { ...defaults, ...options }

// Return the promise from useFetch
return useFetch(url, params)
}

// INCORRECT: Don't await inside the composable
export async function useCustomFetchIncorrect(url, options = {}) {
// This can cause unexpected behavior
const { data } = await useFetch(url, options)
return { data }
}
// CORRECT: Return the promise for the caller to await
export function useCustomFetch(url, options = {}) {
// Set up your custom options
const defaults = { /* your defaults */ }
const params = { ...defaults, ...options }

// Return the promise from useFetch
return useFetch(url, params)
}

// INCORRECT: Don't await inside the composable
export async function useCustomFetchIncorrect(url, options = {}) {
// This can cause unexpected behavior
const { data } = await useFetch(url, options)
return { data }
}
This approach is recommended because awaiting inside the composable can cause unexpected behavior with reactivity Nuxt API: useFetch. Using Your Composable When using your composable in a component, you can decide whether to await it:
<script setup>
// Option 1: Await the result (blocks navigation until resolved)
const { data } = await useCustomFetch('/api/data')

// Option 2: Don't await (allows navigation to proceed)
const { data, pending } = useCustomFetch('/api/data', { lazy: true })
</script>
<script setup>
// Option 1: Await the result (blocks navigation until resolved)
const { data } = await useCustomFetch('/api/data')

// Option 2: Don't await (allows navigation to proceed)
const { data, pending } = useCustomFetch('/api/data', { lazy: true })
</script>
The await in your component determines whether Nuxt will wait for the data before navigation completes. Using await blocks navigation until data is loaded, while the lazy: true option allows navigation to proceed immediately Nuxt Data Fetching: Lazy. Important Context Considerations Nuxt composables must be called in the correct context:
// CORRECT: Composable defined properly
export const useMyComposable = () => {
// This works because useRuntimeConfig is called when the composable is used
const config = useRuntimeConfig()
return { config }
}

// INCORRECT: Trying to use Nuxt composables outside proper context
const config = useRuntimeConfig() // This will fail

export const useMyBrokenComposable = () => {
return { config }
}
// CORRECT: Composable defined properly
export const useMyComposable = () => {
// This works because useRuntimeConfig is called when the composable is used
const config = useRuntimeConfig()
return { config }
}

// INCORRECT: Trying to use Nuxt composables outside proper context
const config = useRuntimeConfig() // This will fail

export const useMyBrokenComposable = () => {
return { config }
}
Nuxt composables must be called synchronously within a Nuxt plugin, route middleware, or Vue setup function Nuxt Concepts: Auto Imports. By following these patterns, you'll create composables that work correctly with Nuxt's reactivity system and SSR capabilities. I hope this helps clarify how to create composables in Nuxt! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@427214835944521728> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Omnislash
Omnislash3mo ago
@kapa.ai can you create a singleton object in a composable instead of a function ?
kapa.ai
kapa.ai3mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Yes, you can create a singleton object in a Nuxt composable. This is particularly useful when you need to maintain a single instance of something across your application. Creating a Singleton in a Composable The key is to define your singleton instance outside the composable function but export the function that provides access to it:
// composables/useCms.ts
import GhostContentAPI from '@tryghost/content-api';

// The API object will be initialized only once
const api = new GhostContentAPI({
url: process.env.GHOST_URL ?? '',
key: process.env.GHOST_API_KEY ?? '',
version: 'v5.0'
});

export default function useCms() {
return api;
};
// composables/useCms.ts
import GhostContentAPI from '@tryghost/content-api';

// The API object will be initialized only once
const api = new GhostContentAPI({
url: process.env.GHOST_URL ?? '',
key: process.env.GHOST_API_KEY ?? '',
version: 'v5.0'
});

export default function useCms() {
return api;
};
Then you can use it in your components or server routes:
// In a component or server route
import useCms from '~/composables/global/useCms';

// Get the singleton instance
const api = useCms();
// In a component or server route
import useCms from '~/composables/global/useCms';

// Get the singleton instance
const api = useCms();
This approach was successfully used in a real-world scenario for initializing third-party services like Ghost CMS Stack Overflow Post > How to maintain singleton dependency injection in Nuxt 3. Important Considerations 1. Be careful with state: If your singleton contains reactive state, use useState instead to ensure proper SSR handling:
// For stateful singletons
export const useMyStore = () => {
// This ensures proper SSR handling
return useState('my-store-key', () => ({
// initial state
}))
}
// For stateful singletons
export const useMyStore = () => {
// This ensures proper SSR handling
return useState('my-store-key', () => ({
// initial state
}))
}
2. Avoid console logging in SSR: Be cautious with logging your singleton to the console in SSR mode, as it can cause memory leaks when using the node inspector Github Issue: useState + SSR = memory leak. 3. Best practices: Follow Nuxt's recommendation to never define reactive references outside of <script setup> or setup() functions to avoid state being shared across requests on the server Docs > Getting-started > State Management. This pattern is particularly useful for services like API clients, database connections, or other resources that should be initialized once and shared throughout your application. Would you like more specific examples for your use case? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai3mo ago
<@305772639438503936> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?