Migration Guide
This guide helps you migrate your Nuxt Crouton projects from older versions to the latest release.
v1.x → v2.0
Version 2.0 introduced significant changes to improve performance and developer experience.
Breaking Changes Overview
- ✅
send()method removed - ✅ Global state management removed
- ✅ Button component API updated
- ✅ Cache invalidation improved
Breaking Change 1: send() Removed
What Changed
The send() method from useCrouton() has been removed and replaced with two new approaches:
useCroutonMutate()- For quick, one-off mutationsuseCollectionMutation()- For optimized mutations in forms
Before (v1.x)
<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>
After v2.0 - Option 1: useCroutonMutate (Quick)
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>
After v2.0 - Option 2: useCollectionMutation (Optimized)
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>
When to Use Which?
| Use Case | Use This |
|---|---|
| Toggle button | useCroutonMutate() |
| Quick add/remove | useCroutonMutate() |
| Utility function | useCroutonMutate() |
| Generated forms | useCollectionMutation() |
| Multi-step wizard | useCollectionMutation() |
| Bulk operations | useCollectionMutation() |
Breaking Change 2: Global State Removed
What Changed
The global reactive collections state (useCollections()) has been removed in favor of useCollectionQuery() with proper caching.
Before (v1.x)
<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>
After v2.0
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>
Benefits
- ✅ Automatic cache management
- ✅ Automatic refetching after mutations
- ✅ Loading and error states built-in
- ✅ No manual state synchronization
Breaking Change 3: Button Component Updated
What Changed
CroutonButton no longer accepts a @submit handler. Form submission is now handled by the UForm component.
Before (v1.x)
<template>
<UForm :state="state" :schema="schema">
<UFormField label="Name" name="name">
<UInput v-model="state.name" />
</UFormField>
<CroutonButton
:action="action"
:collection="collection"
@submit="send(action, collection, state)"
/>
</UForm>
</template>
<script setup lang="ts">
const { send } = useCrouton()
const state = ref({ name: '' })
</script>
After v2.0
<template>
<UForm
:state="state"
:schema="schema"
@submit="handleSubmit"
>
<UFormField label="Name" name="name">
<UInput v-model="state.name" />
</UFormField>
<CroutonButton
: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>
Migration Steps
Step 1: Update Dependencies
# Update to latest version
pnpm update @friendlyinternet/nuxt-crouton
pnpm update @friendlyinternet/nuxt-crouton-collection-generator
pnpm update @friendlyinternet/nuxt-crouton-i18n # If using i18n
Step 2: Backup Generated Code
# Create backup of your customizations
cp -r layers layers.backup
Step 3: Update Generated Forms
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:
- Replace
useCrouton().send()withuseCollectionMutation() - Add
handleSubmitfunction - Update
CroutonButtonprops
See Breaking Change 1 and Breaking Change 3 for examples.
Step 4: Update Custom Code
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)
Step 5: Update Global State Usage
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.
Step 6: Test Thoroughly
- Test CRUD operations:
- Create new items
- Update existing items
- Delete items
- Verify data refreshes automatically
- Test forms:
- Open create form
- Open edit form
- Submit forms
- Check validation
- Test caching:
- Verify data loads from cache
- Verify cache invalidates after mutations
- Check multiple views update together
Step 7: Clean Up
# Remove backup if everything works
rm -rf layers.backup
# Clear caches
rm -rf .nuxt node_modules/.cache
# Restart dev server
pnpm dev
Common Migration Issues
Issue: "send is not a function"
Cause: Using old form with new core library
Fix: Regenerate form or manually update (see Step 3)
Issue: Data not refreshing
Cause: Old cache invalidation logic
Fix: Ensure using useCollectionMutation() which handles invalidation automatically
Issue: Type errors
Cause: Type definitions changed
Fix:
rm -rf .nuxt
npx nuxt prepare
# Restart TS server in VS Code
Issue: Forms not submitting
Cause: Missing @submit handler on UForm
Fix: Add @submit="handleSubmit" to UForm component
Need Help?
If you encounter issues during migration:
- Check GitHub Issues: github.com/pmcp/nuxt-crouton/issues
- Search Discussions: github.com/pmcp/nuxt-crouton/discussions
- Create an Issue: Include:
- Version you're migrating from
- Version you're migrating to
- Error messages
- Code samples
Related Resources
- API Reference - New API documentation
- Troubleshooting - Common issues and fixes
- Best Practices - Recommended patterns
- Working with Data - Mutation guide