useCollectionQuery patterns (basic, filtering, pagination, sorting, relations), see Querying Data.This page answers the most frequently asked questions about Nuxt Crouton. For detailed troubleshooting, see the Troubleshooting Guide.
A: Never define these fields in your schema files:
id - Always auto-generated (UUID or nanoid)teamId, owner - Always auto-generated (team-scoped by default)createdAt, updatedAt, createdBy, updatedBy - Generated when useMetadata: true (default)Defining these manually causes duplicate key errors during build.
Common mistake:
{
"id": { "type": "string" }, // ❌ Remove
"createdAt": { "type": "date" }, // ❌ Remove
"teamId": { "type": "string" }, // ❌ Remove
"title": { "type": "string" } // ✅ Keep
}
See: Schema Format - Auto-Generated Fields
A: Nuxt Crouton generates code in layers, which you can freely customize. Regeneration is safe as long as you:
components/custom/# Safe regeneration workflow
git add .
git commit -m "Before regeneration"
npx crouton config ./crouton.config.js --only products --force
See: Customization
A: Always use plural names:
✅ Correct: products, blogPosts, orderItems
❌ Wrong: product (singular), blog_post (snake_case), orderItem (singular)
See: Conventions - Collection Names
A: Use camelCase for all field names:
{
"firstName": { "type": "string" }, // ✅ Correct
"isActive": { "type": "boolean" }, // ✅ Correct
"publishedAt": { "type": "date" }, // ✅ Correct
"first_name": { "type": "string" }, // ❌ Wrong (snake_case)
"FirstName": { "type": "string" } // ❌ Wrong (PascalCase)
}
See: Conventions - Field Names
A: This is usually a cache invalidation issue. Check:
useCollectionMutation() which auto-invalidates cache// ✅ Correct - auto-invalidation
const { create, update } = useCollectionMutation('products')
await create({ title: 'New Product' })
// ✅ Also correct - useCroutonMutate auto-invalidates too
const { mutate } = useCroutonMutate()
await mutate('create', 'products', data)
// Cache is auto-invalidated internally, no manual refresh needed
See: Troubleshooting - Data Not Updating
A: All query composables return pending and error properties. Use these to conditionally render loading spinners, error messages, and retry buttons. For the complete pattern, see Best Practices - Handle Loading States.
useCollectionMutation() vs useCroutonMutate()?A:
Use useCollectionMutation() for:
Use useCroutonMutate() for:
See: Best Practices - Choose the Right Mutation Method
A: Edit the generated _Form.vue component directly. It lives in your project at layers/[layer]/collections/[collection]/app/components/_Form.vue and is yours to modify. You can change field components, add custom logic, or rearrange the layout.
See: Customization - Custom Components
A: Edit the generated List.vue component directly. It lives in your project at layers/[layer]/collections/[collection]/app/components/List.vue and is yours to modify.
See: Customization - Custom Columns
A: Yes, create a component with the same name in your layer:
layers/shop/collections/products/app/components/
├── _Form.vue ← Generated form, yours to edit
└── List.vue ← Generated list, yours to edit
Since generated code lives in your project, you can edit it directly.
A: TypeScript server needs to be restarted:
In VS Code:
Cmd+Shift+P (Mac) or Ctrl+Shift+P (Windows)Or clear cache:
rm -rf .nuxt
npx nuxt prepare
See: Troubleshooting - Type Errors
A: Use TypeScript generics with useCollectionQuery<YourType>() to get full type safety. Import your type from the layer's types file and pass it as a generic parameter. See: Best Practices - Type Your Queries.
A: Tailwind v4 doesn't auto-scan node_modules. Add @source directive:
/* app/assets/css/tailwind.css */
@import "tailwindcss";
@import "@nuxt/ui";
/* Scan Nuxt Crouton layers */
@source "../../../node_modules/@fyit/crouton*/app/**/*.{vue,js,ts}";
Then restart dev server:
pnpm dev
See: Troubleshooting - Tailwind Classes
A: Use string type with refTarget:
{
"authorId": { "type": "string", "refTarget": "users" }
}
See: Patterns - Relations
A: Use server-side joins in your API endpoints to load related data. See Querying with Relations for the complete pattern and Patterns - Relations for advanced usage.
A: Check that:
:schema="schema"ref() or reactive())<script setup lang="ts">
import { z } from 'zod'
const schema = z.object({
name: z.string().min(1, 'Required'),
price: z.number().min(0)
})
const state = ref({ name: '', price: 0 })
</script>
<template>
<UForm :state="state" :schema="schema" @submit="handleSubmit">
<UFormField label="Name" name="name">
<UInput v-model="state.name" />
</UFormField>
</UForm>
</template>
See: Troubleshooting - Validation Errors
A: Use v-if with reactive state:
<script setup lang="ts">
const productType = ref('physical')
</script>
<template>
<UFormField label="Type" name="type">
<USelect v-model="productType" :options="['physical', 'digital']" />
</UFormField>
<!-- Only show for physical products -->
<UFormField v-if="productType === 'physical'" label="Weight" name="weight">
<UInput v-model="formData.weight" type="number" />
</UFormField>
</template>
See: Patterns - Forms
A: Use these strategies:
See: Troubleshooting - Performance Issues
A:
See: Features Overview
A:
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
'@fyit/crouton',
'@fyit/crouton-i18n'
]
})
// crouton.config.js
export default {
translations: {
collections: {
products: ['name', 'description']
}
}
}
See: Internationalization
A: No, generated code is committed to your repository. Just deploy as normal:
pnpm build
A: Depends on your setup:
Database:
hub: { db: 'sqlite' } in nuxt.config.ts, no DATABASE_URL needed)Team-based auth:
BETTER_AUTH_SECRET - Session encryption (32+ chars)BETTER_AUTH_URL - Production URLFeatures:
NUXT_PUBLIC_I18N_DEFAULT_LOCALECheck your layer's documentation for specific requirements.
A: