Composables
Composables
Auto-imported composables available in your admin components and custom pages.
Admin Composables
useAdminRegistry
Access all registered resources and their schemas.
const {
registry, // ComputedRef<Record<string, ResourceSchema>>
allResources, // ComputedRef<ResourceSchema[]> (sorted by `order`)
getResourcesByGroup, // ComputedRef<Record<string, ResourceSchema[]>>
getResource, // (name: string) => ResourceSchema | undefined
isLoading, // always false — the registry is bundled
} = useAdminRegistry()
Example:
<script setup>
const { allResources, getResourcesByGroup } = useAdminRegistry()
// Get all resources
console.log(allResources.value)
// Get resources by group
const grouped = getResourcesByGroup.value
// { 'Content': [...], 'User Management': [...] }
// Get specific resource
const { getResource } = useAdminRegistry()
const usersResource = getResource('users')
</script>
useAdminResource
Get schema information for a specific resource.
const {
resource, // Ref<ResourceSchema | undefined>
isLoading, // Ref<boolean>
} = useAdminResource(resourceName)
Example:
<script setup>
const { resource } = useAdminResource('users') // a plain string — the resource is looked up once
// Access schema
console.log(resource.value?.displayName) // 'Users'
console.log(resource.value?.icon) // 'i-heroicons-user-group'
console.log(resource.value?.columns) // Column metadata
console.log(resource.value?.formFields) // Form configuration
</script>
useAdminPermissions
Check permissions for a resource.
const {
canCreate, // Ref<boolean>
canRead, // Ref<boolean>
canUpdate, // Ref<boolean>
canDelete, // Ref<boolean>
hasAnyPermission, // Ref<boolean>
isLoading, // Ref<boolean>
getPermissionDeniedMessage, // (action: string) => string
} = useAdminPermissions(resourceName)
Example:
<template>
<div>
<UButton v-if="canCreate" @click="createNew">
Create New
</UButton>
<UButton v-if="canUpdate" :disabled="!canUpdate" @click="edit">
Edit
</UButton>
</div>
</template>
<script setup>
const { canCreate, canUpdate, canDelete } = useAdminPermissions('posts')
</script>
useAdminActions
Get common navigation and action functions for a resource.
const {
goToList, // () => void
goToDetail, // (id: string | number) => void
goToEdit, // (id: string | number) => void
goToCreate, // () => void
handleDelete, // (id: string | number, options?: { redirect?: boolean }) => Promise<void>
isDeleting, // Ref<boolean>
} = useAdminActions(resourceName)
Example:
<template>
<div>
<UButton @click="goToList">Back to List</UButton>
<UButton @click="goToEdit(user.id)">Edit</UButton>
<UButton
:loading="isDeleting"
@click="handleDelete(user.id, { redirect: true })"
>
Delete
</UButton>
</div>
</template>
<script setup>
const props = defineProps<{ user: any }>()
const { goToList, goToEdit, handleDelete, isDeleting } = useAdminActions('users')
</script>
useResourceForm
Generate form fields and initial data for a resource.
const {
fields, // Ref<FieldConfig[]>
initialData, // Ref<Record<string, any>>
isLoading, // Ref<boolean>
resource, // Ref<ResourceSchema | undefined>
} = useResourceForm(resourceName, mode) // mode: 'create' | 'edit'
Example:
<script setup>
const { fields, initialData } = useResourceForm('posts', 'create')
// Use in custom form
const formData = ref(initialData.value)
</script>
useAdminConfig
Access admin configuration.
const {
prefix, // string - Admin route prefix
branding, // { title, logo }
permissions, // { unauthorizedButtons, unauthorizedSidebarItems }
features, // { bulkActions, search, filters, ... }
customPages, // CustomPageConfig[]
ui, // { editMode, viewMode }
api, // what nuxt-auto-api offers: { maxLimit, bulk, maxBatchSize, export, auditLog }
} = useAdminConfig()
Example:
<script setup>
const { branding, permissions } = useAdminConfig()
console.log(branding.title) // 'My Admin Panel'
const showDisabledButtons = permissions.unauthorizedButtons === 'disable'
</script>
Auto-API Composables
These composables are from @websideproject/nuxt-auto-api and work in the admin panel:
useAutoApiList
Query a list of resources.
const {
data, // Ref<ListResponse<T> | undefined>
isLoading, // Ref<boolean>
error, // Ref<Error | null>
refetch, // () => Promise<void>
} = useAutoApiList(resource, params, options)
Example:
<script setup>
const params = reactive({
filter: { status: 'published' },
sort: '-createdAt',
page: 1,
limit: 20,
})
const { data, isLoading } = useAutoApiList('posts', params)
// Access results
const posts = computed(() => data.value?.data || [])
const total = computed(() => data.value?.meta?.total)
</script>
useAutoApiGet
Get a single resource by ID.
const {
data, // Ref<GetResponse<T> | undefined>
isLoading, // Ref<boolean>
error, // Ref<Error | null>
refetch, // () => Promise<void>
} = useAutoApiGet(resource, id, params, options)
Example:
<script setup>
const route = useRoute()
const id = computed(() => route.params.id as string)
const { data, isLoading } = useAutoApiGet('posts', id, {
include: 'author,comments'
})
// Access record
const post = computed(() => data.value?.data)
</script>
useAutoApiCreate
Create a new resource.
const {
mutate, // (data: T) => void
mutateAsync, // (data: T) => Promise<any>
isPending, // Ref<boolean>
isSuccess, // Ref<boolean>
error, // Ref<Error | null>
} = useAutoApiCreate(resource)
Example:
<script setup>
const { mutate: createPost, isPending } = useAutoApiCreate('posts')
function handleSubmit(formData) {
createPost(formData, {
onSuccess: (response) => {
console.log('Created:', response.data)
navigateTo(`/admin/posts/${response.data.id}`)
},
onError: (error) => {
console.error('Failed:', error)
},
})
}
</script>
useAutoApiUpdate
Update an existing resource.
const {
mutate, // ({ id, ...fields }) => void — the fields go next to the id, not under `data`
mutateAsync,
isPending,
isSuccess,
error,
} = useAutoApiUpdate(resource)
Example:
<script setup>
const { mutate: updatePost, isPending } = useAutoApiUpdate('posts')
const toast = useToast()
function handleSubmit(formData) {
updatePost(
{ id: postId.value, ...formData },
{
onSuccess: () => {
toast.add({ title: 'Post updated', color: 'success' })
},
}
)
}
</script>
useAutoApiDelete
Delete a resource.
const {
mutate, // (id: string | number) => void
mutateAsync,
isPending,
isSuccess,
error,
} = useAutoApiDelete(resource)
Example:
<script setup>
const { mutate: deletePost, isPending: isDeleting } = useAutoApiDelete('posts')
const toast = useToast()
function confirmDelete() {
deletePost(postId.value, {
onSuccess: () => {
toast.add({ title: 'Post deleted', color: 'success' })
navigateTo('/admin/posts')
},
})
}
</script>
usePermissions
Get permissions from auto-api (used internally by useAdminPermissions).
const {
permissions, // ComputedRef<PermissionCheckResult | undefined> — includes canRestore, canPurge, canViewDeleted, fields
canCreate,
canRead,
canUpdate,
canDelete,
isLoading,
} = usePermissions(resource)
useAutoApiToast
Show messages through the toast provider the admin registers (the same one the mutations' toast option uses):
const { handleSuccess, handleError, handleWarning, handleInfo, isAvailable } = useAutoApiToast()
handleSuccess('Operation successful', 'Optional description')
handleError(error) // title from the HTTP status, description from the API's message
handleInfo('Information message')
handleWarning('Warning message')
Complete Custom Page Example
<!-- pages/admin/analytics.vue -->
<template>
<div class="space-y-4">
<h1 class="text-2xl font-semibold">Analytics Dashboard</h1>
<div v-if="isLoadingStats" class="text-center p-8">
<UIcon name="i-heroicons-arrow-path" class="animate-spin h-8 w-8" />
</div>
<div v-else class="grid grid-cols-1 md:grid-cols-3 gap-4">
<UCard>
<div class="text-center">
<div class="text-3xl font-bold">{{ stats.users }}</div>
<div class="text-sm text-muted">Total Users</div>
</div>
</UCard>
<UCard>
<div class="text-center">
<div class="text-3xl font-bold">{{ stats.posts }}</div>
<div class="text-sm text-muted">Total Posts</div>
</div>
</UCard>
<UCard>
<div class="text-center">
<div class="text-3xl font-bold">{{ stats.comments }}</div>
<div class="text-sm text-muted">Total Comments</div>
</div>
</UCard>
</div>
<UCard>
<h2 class="text-lg font-semibold mb-4">Recent Posts</h2>
<div v-if="isLoadingPosts" class="text-center p-4">Loading...</div>
<div v-else>
<div
v-for="post in recentPosts"
:key="post.id"
class="flex items-center justify-between p-3 border-b last:border-0"
>
<div>
<div class="font-medium">{{ post.title }}</div>
<div class="text-sm text-muted">
{{ new Date(post.createdAt).toLocaleString() }}
</div>
</div>
<UButton
size="xs"
variant="ghost"
@click="goToPostDetail(post.id)"
>
View
</UButton>
</div>
</div>
</UCard>
</div>
</template>
<script setup lang="ts">
definePageMeta({
layout: 'admin',
})
// Get stats (each needs the caller's `aggregate` permission — falls back to `read`)
const users = useAutoApiList('users', { aggregate: 'count', limit: 1 })
const posts = useAutoApiList('posts', { aggregate: 'count', limit: 1 })
const comments = useAutoApiList('comments', { aggregate: 'count', limit: 1 })
const isLoadingStats = computed(() => users.isLoading.value || posts.isLoading.value || comments.isLoading.value)
const stats = computed(() => ({
users: users.data.value?.meta.aggregates?.count ?? 0,
posts: posts.data.value?.meta.aggregates?.count ?? 0,
comments: comments.data.value?.meta.aggregates?.count ?? 0,
}))
// Get recent posts
const {
data: postsResponse,
isLoading: isLoadingPosts,
} = useAutoApiList('posts', {
sort: '-createdAt',
limit: 5,
})
const recentPosts = computed(() => postsResponse.value?.data || [])
// Navigation
const { goToDetail } = useAdminActions('posts')
const goToPostDetail = (id: number) => goToDetail(id)
</script>
Next Steps
- Custom Pages - Build custom admin pages
- Custom Actions - Add custom resource actions
Custom Actions
Add your own buttons next to the built-in ones: publish a draft, archive the selected rows, sync a resource with another system.
Overview
Install the nuxt-auto Claude Code skill plugin to give Claude accurate API knowledge for nuxt-auto-api and nuxt-auto-admin.