Using a custom component in Nuxt UI form

Hi there. I've got a USelectMenu wrapped in a component so I can reuse its logic - it gets its items from a db. how can i use this custom component in a nuxt ui UForm in the same way I would a plain USelectMenu? Thanks 🙂
11 Replies
kapa.ai
kapa.ai2w ago
Hi @MrSandyWilly. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To use your custom component (which wraps a USelectMenu) inside a Nuxt UI UForm, you need to ensure that your component integrates properly with the form system. The key is to make your custom component compatible with v-model and, for full integration (like validation and error handling), to use the useFormField composable provided by Nuxt UI. Here’s how you can approach it: 1. Expose v-model: Your custom component should accept a modelValue prop and emit update:modelValue events, just like USelectMenu does. This allows UForm and UFormField to bind to it as they would with any native Nuxt UI input. 2. Integrate with useFormField: Use the useFormField composable inside your custom component. This will allow your component to communicate with the form context for things like validation, error display, and more. Example of a custom wrapper component:
<script setup lang="ts">
import { useFormField } from '#imports'
const props = defineProps({
modelValue: [String, Object, Array],
// ...other props you want to pass through
})
const emit = defineEmits(['update:modelValue'])

const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()

function onUpdate(value) {
emit('update:modelValue', value)
emitFormInput(value)
emitFormChange(value)
}
</script>

<template>
<USelectMenu
:id="id"
:model-value="modelValue"
@update:modelValue="onUpdate"
@blur="emitFormBlur"
<!-- pass through other props as needed -->
v-bind="$attrs"
/>
</template>
<script setup lang="ts">
import { useFormField } from '#imports'
const props = defineProps({
modelValue: [String, Object, Array],
// ...other props you want to pass through
})
const emit = defineEmits(['update:modelValue'])

const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()

function onUpdate(value) {
emit('update:modelValue', value)
emitFormInput(value)
emitFormChange(value)
}
</script>

<template>
<USelectMenu
:id="id"
:model-value="modelValue"
@update:modelValue="onUpdate"
@blur="emitFormBlur"
<!-- pass through other props as needed -->
v-bind="$attrs"
/>
</template>
Usage in a form:
<UForm>
<UFormField label="Status" name="status" required>
<MyCustomSelectMenu v-model="form.status" :items="items" />
</UFormField>
</UForm>
<UForm>
<UFormField label="Status" name="status" required>
<MyCustomSelectMenu v-model="form.status" :items="items" />
</UFormField>
</UForm>
This setup ensures your custom component works seamlessly with UForm and UFormField, just like a plain USelectMenu would. For more details on integrating custom inputs with forms, see the useFormField composable documentation. If you need an example of using a standard input within a form field, see the Input within a FormField example. If you need more advanced validation or error handling, ensure your custom component passes through any relevant props and events from UFormField/UForm as needed.
If you need a more specific example or run into issues, please provide your custom component code and I can help you adapt it!
---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@744626724641177630> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
MrSandyWilly
MrSandyWillyOP2w ago
@kapa.ai apparentkly emitFormInput and emitFormChange dont take arguments
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- You're correct—according to the official Nuxt UI documentation, the usage of emitFormInput, emitFormChange, and emitFormBlur from the useFormField composable is shown without arguments. The documentation does not specify that these functions accept any parameters, nor does it show passing the new value to them. Their purpose is to notify the form context of the respective events, not to pass the value itself. You should still emit the update:modelValue event with the new value for v-model binding, but call the form field emitters without arguments:
<script setup lang="ts">
const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()
const emit = defineEmits(['update:modelValue'])

function onUpdate(value) {
emit('update:modelValue', value)
emitFormInput()
emitFormChange()
}
</script>
<script setup lang="ts">
const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()
const emit = defineEmits(['update:modelValue'])

function onUpdate(value) {
emit('update:modelValue', value)
emitFormInput()
emitFormChange()
}
</script>
This approach matches the example provided in the useFormField composable documentation, which does not show arguments being passed to these functions. If you need to pass the value, you should only do so via the update:modelValue event. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@744626724641177630> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
MrSandyWilly
MrSandyWillyOP2w ago
still doesnt work... @kapa.ai here is my current code in the component
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui';

const props = defineProps({
modelValue: [String, Object, Array]
})

const supabase = useSupabaseClient();
const emit = defineEmits(['update:modelValue'])

const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()

function onUpdate(value: SelectMenuItem) {
console.log(value)
emit('update:modelValue', value)
emitFormInput()
emitFormChange()
}

const status = ref("pending")
const categories: Ref<SelectMenuItem[]> = ref([])

onMounted(async () => {
let { data: activity_categories, error } = await supabase
.from('activity_categories')
.select('id, name')

if (error) {
status.value = "error"
console.log(error)
return
}

if (activity_categories) {
categories.value = activity_categories.map(cat => ({
label: cat.name as string,
value: cat.id as string
}))
status.value = "completed"
}
})

</script>
<template>
<USelectMenu :items="categories" :loading="status === 'pending'" placeholder="Select category" :icon="status === 'error' ? 'i-lucide-circle-x' : ''" :id="id" :model-value="modelValue" @update:model-value="onUpdate" @blur="emitFormBlur"></USelectMenu>
</template>
<script setup lang="ts">
import type { SelectMenuItem } from '@nuxt/ui';

const props = defineProps({
modelValue: [String, Object, Array]
})

const supabase = useSupabaseClient();
const emit = defineEmits(['update:modelValue'])

const { id, emitFormBlur, emitFormInput, emitFormChange } = useFormField()

function onUpdate(value: SelectMenuItem) {
console.log(value)
emit('update:modelValue', value)
emitFormInput()
emitFormChange()
}

const status = ref("pending")
const categories: Ref<SelectMenuItem[]> = ref([])

onMounted(async () => {
let { data: activity_categories, error } = await supabase
.from('activity_categories')
.select('id, name')

if (error) {
status.value = "error"
console.log(error)
return
}

if (activity_categories) {
categories.value = activity_categories.map(cat => ({
label: cat.name as string,
value: cat.id as string
}))
status.value = "completed"
}
})

</script>
<template>
<USelectMenu :items="categories" :loading="status === 'pending'" placeholder="Select category" :icon="status === 'error' ? 'i-lucide-circle-x' : ''" :id="id" :model-value="modelValue" @update:model-value="onUpdate" @blur="emitFormBlur"></USelectMenu>
</template>
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Your component implementation is correct and aligns well with the Nuxt UI form integration guidelines. Here’s a review based on the official documentation: - useFormField: You are correctly using the useFormField composable to get the id, emitFormBlur, emitFormInput, and emitFormChange functions. According to the documentation, these functions do not take arguments and are used to notify the form context of input, change, and blur events, which is exactly how you are using them see composable usage. - v-model compatibility: You are emitting update:modelValue with the new value, which is required for v-model compatibility in both standalone and form usage. - Form integration: By passing the id prop and emitting the correct events, your component will work seamlessly inside a UFormField and UForm, just like a native USelectMenu see Input within a FormField. Summary:
Your approach is correct and matches the Nuxt UI documentation for custom input integration with forms. You do not need to pass arguments to emitFormInput or emitFormChange—just call them as you do now. Your component will work as a drop-in replacement for USelectMenu inside a Nuxt UI form. If you want to see a minimal example of this pattern, refer to the useFormField composable documentation. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@744626724641177630> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
MrSandyWilly
MrSandyWillyOP2w ago
@kapa.ai then why isnt it working
kapa.ai
kapa.ai2w ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- I'm sorry, but the knowledge sources provided do not contain enough information to diagnose why your custom component is not working as expected within a Nuxt UI form. The sources confirm the correct approach for integrating custom components with UForm and UFormField using useFormField and emitting update:modelValue events, but they do not address specific troubleshooting steps or common pitfalls for cases where the integration fails or does not behave as intended useFormField composable. If you can provide more details about what "isn't working" (e.g., is the value not updating, is validation not triggering, are there errors in the console?), I may be able to help further. As it stands, the knowledge sources do not explicitly mention reasons for such issues or offer debugging guidance for this scenario. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@744626724641177630> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?