This guide helps you migrate your Nuxt Crouton projects from older versions to the latest release.
If you're upgrading from a project that used the useTeamUtility flag or the #crouton/team-auth alias, follow this section.
useTeamUtility flag removed from generator#crouton/team-auth alias replaced with @fyit/crouton-auth/server/utils/team imports@fyit/crouton-auth is now required for team authenticationThe useTeamUtility configuration flag has been removed. All generated collections now use team-scoped authentication by default.
// crouton.config.js
export default {
flags: {
useTeamUtility: true // or false for non-team collections
}
}
// crouton.config.js
export default {
// No useTeamUtility flag - all collections are team-scoped
}
Generated API endpoints now import team auth utilities from @fyit/crouton-auth/server/utils/team instead of using the #crouton/team-auth alias.
import { resolveTeamAndCheckMembership } from '#crouton/team-auth'
import { resolveTeamAndCheckMembership } from '@fyit/crouton-auth/server/utils/team'
pnpm add @fyit/crouton-auth
// nuxt.config.ts
export default defineNuxtConfig({
extends: [
'@fyit/crouton',
'@fyit/crouton-auth' // Add this if not already present
]
})
Ensure your database schema exports the auth tables:
// server/database/schema/index.ts
export * from '@fyit/crouton-auth/server/database/schema/auth'
Regenerate your collections to update the import paths:
# Backup your customizations first
cp -r layers layers.backup
# Regenerate with new imports
npx crouton-generate config ./crouton.config.js --force
Then restore any customizations from layers.backup/.
If you have custom API endpoints using the old import:
# Find all old imports
grep -r "#crouton/team-auth" layers/
Replace them:
// Before
import { resolveTeamAndCheckMembership } from '#crouton/team-auth'
// After
import { resolveTeamAndCheckMembership } from '@fyit/crouton-auth/server/utils/team'
If you were using route.params.team directly, switch to useTeamContext():
// Before
const route = useRoute()
const teamId = route.params.team
// After
const { teamId, teamSlug } = useTeamContext()
If you had a manual nitro alias, remove it:
// nuxt.config.ts - REMOVE this if present
export default defineNuxtConfig({
nitro: {
alias: {
'#crouton/team-auth': '@fyit/crouton-auth/server/utils/team-auth' // Remove
}
}
})
The @fyit/crouton-auth/server/utils/team import works directly without aliases.
# Type check
npx nuxt typecheck
# Test CRUD operations
pnpm dev
Cause: @fyit/crouton-auth not installed or not extending the layer
Fix:
pnpm add @fyit/crouton-auth
And ensure it's in your extends array in nuxt.config.ts.
Cause: Using old alias or package version mismatch
Fix: Update to the latest @fyit/crouton-auth:
pnpm update @fyit/crouton-auth
Cause: Route doesn't have team parameter
Fix: Ensure your routes include [team] parameter:
pages/
└── [team]/
└── products/
└── index.vue
Version 2.0 introduced significant changes to improve performance and developer experience.
send() method removedThe send() method from useCrouton() has been removed and replaced with two new approaches:
useCroutonMutate() - For quick, one-off mutationsuseCollectionMutation() - For optimized mutations in forms<script setup lang="ts">
const { send } = useCrouton()
const handleCreate = async () => {
await send('create', 'shopProducts', {
name: 'New Product',
price: 29.99
})
}
const handleUpdate = async (id: string) => {
await send('update', 'shopProducts', {
id,
name: 'Updated Name'
})
}
const handleDelete = async (ids: string[]) => {
await send('delete', 'shopProducts', ids)
}
</script>
For quick actions, toggle buttons, and utility functions:
<script setup lang="ts">
const { mutate } = useCroutonMutate()
const handleCreate = async () => {
await mutate('create', 'shopProducts', {
name: 'New Product',
price: 29.99
})
}
const handleUpdate = async (id: string) => {
await mutate('update', 'shopProducts', {
id,
name: 'Updated Name'
})
}
const handleDelete = async (ids: string[]) => {
await mutate('delete', 'shopProducts', ids)
}
</script>
For forms and repeated operations on the same collection:
<script setup lang="ts">
const { create, update, deleteItems } = useCollectionMutation('shopProducts')
const handleCreate = async () => {
await create({
name: 'New Product',
price: 29.99
})
}
const handleUpdate = async (id: string, data: any) => {
await update(id, data)
}
const handleDelete = async (ids: string[]) => {
await deleteItems(ids)
}
</script>
| Use Case | Use This |
|---|---|
| Toggle button | useCroutonMutate() |
| Quick add/remove | useCroutonMutate() |
| Utility function | useCroutonMutate() |
| Generated forms | useCollectionMutation() |
| Multi-step wizard | useCollectionMutation() |
| Bulk operations | useCollectionMutation() |
The global reactive collections state (useCollections()) has been removed in favor of useCollectionQuery() with proper caching.
<script setup lang="ts">
const { shopProducts } = useCollections()
// Manual fetch
const { data } = await useFetch('/api/products')
shopProducts.value = data.value
// Manual updates
watch(shopProducts, () => {
// React to changes
})
</script>
<template>
<div v-for="product in shopProducts" :key="product.id">
{{ product.name }}
</div>
</template>
useCollectionQuery patterns including filters, pagination, and sorting, see Querying Data.<script setup lang="ts">
// Automatic fetching and caching
const { items, pending, refresh } = await useCollectionQuery('shopProducts')
// Automatic refetch after mutations
const { create } = useCollectionMutation('shopProducts')
await create({ name: 'New Product' })
// → items automatically updates
</script>
<template>
<div v-if="pending">Loading...</div>
<div v-else>
<div v-for="product in items" :key="product.id">
{{ product.name }}
</div>
</div>
</template>
CroutonFormActionButton no longer accepts a @submit handler. Form submission is now handled by the UForm component.
<template>
<UForm :state="state" :schema="schema">
<UFormField label="Name" name="name">
<UInput v-model="state.name" />
</UFormField>
<CroutonFormActionButton
:action="action"
:collection="collection"
@submit="send(action, collection, state)"
/>
</UForm>
</template>
<script setup lang="ts">
const { send } = useCrouton()
const state = ref({ name: '' })
</script>
<template>
<UForm
:state="state"
:schema="schema"
@submit="handleSubmit"
>
<UFormField label="Name" name="name">
<UInput v-model="state.name" />
</UFormField>
<CroutonFormActionButton
:action="action"
:collection="collection"
:loading="loading"
type="submit"
/>
</UForm>
</template>
<script setup lang="ts">
const props = defineProps<{
action: 'create' | 'update' | 'delete'
collection: string
}>()
const { create, update, deleteItems } = useCollectionMutation(props.collection)
const state = ref({ name: '' })
const handleSubmit = async () => {
if (props.action === 'create') {
await create(state.value)
} else if (props.action === 'update') {
await update(state.value.id, state.value)
} else if (props.action === 'delete') {
await deleteItems(props.items)
}
close()
}
</script>
# Update to latest version
pnpm update @fyit/crouton
pnpm update @fyit/crouton-cli
pnpm update @fyit/crouton-i18n # If using i18n
# Create backup of your customizations
cp -r layers layers.backup
Option A: Regenerate (Recommended for standard forms)
# Regenerate all collections
npx crouton-generate config ./crouton.config.js --force
Then restore any customizations from layers.backup/.
Option B: Manual Update (If heavily customized)
Update each Form.vue manually:
useCrouton().send() with useCollectionMutation()handleSubmit functionCroutonFormActionButton propsSee Breaking Change 1 and Breaking Change 3 for examples.
Find and replace all instances of send():
# Find all usages
grep -r "const.*send.*=.*useCrouton" layers/
# Replace with useCroutonMutate or useCollectionMutation
Quick actions:
// Before
const { send } = useCrouton()
await send('update', 'products', data)
// After
const { mutate } = useCroutonMutate()
await mutate('update', 'products', data)
Forms:
// Before
const { send } = useCrouton()
await send('create', 'products', data)
// After
const { create } = useCollectionMutation('products')
await create(data)
Replace useCollections() with useCollectionQuery():
<!-- Before -->
<script setup>
const { shopProducts } = useCollections()
</script>
<!-- After -->
<script setup>
const { items: shopProducts } = await useCollectionQuery('shopProducts')
</script>
See Querying Data for advanced query patterns.
# Remove backup if everything works
rm -rf layers.backup
# Clear caches
rm -rf .nuxt node_modules/.cache
# Restart dev server
pnpm dev
Cause: Using old form with new core library
Fix: Regenerate form or manually update (see Step 3)
Cause: Old cache invalidation logic
Fix: Ensure using useCollectionMutation() which handles invalidation automatically
Cause: Type definitions changed
Fix:
rm -rf .nuxt
npx nuxt prepare
# Restart TS server in VS Code
Cause: Missing @submit handler on UForm
Fix: Add @submit="handleSubmit" to UForm component
If you encounter issues during migration: