Common UI Components
Work in progress
This page is a work in progress. Help complete it by contributing on GitHub!
- Component Categories
- Component Patterns
- UIkit Integration
- Best Practices
- What's Next?
- Further Reading
UserFrosting 6.0 provides patterns and examples for building common UI components with Vue 3. This section covers the most frequently used interactive elements in web applications.
Component Categories
Forms
Handle user input with validated, AJAX-powered forms. Learn how to:
- Build reactive forms with two-way data binding
- Implement client and server-side validation
- Submit data with proper CSRF protection
- Display loading states and error messages
- Create reusable form composables
Tables
Display and manipulate collections of data with sortable, filterable, paginated tables:
- Client-side sorting and filtering
- Server-side pagination with Sprunjes
- Integration with TanStack Table library
- Reusable table composables
- Loading states and empty states
Collections
Manage dynamic lists of items where users can add, remove, and reorder entries:
- Dynamic form fields (add/remove)
- Complex collection items with multiple fields
- Drag-and-drop reordering
- Validation for collection items
- Server integration patterns
Learn more about Collections →
Alerts and Notifications
Provide user feedback through alerts, notifications, and toast messages:
- UIkit notification system
- Alert stream integration
- Reusable alert composables
- Error handling patterns
- Confirmation dialogs
Component Patterns
Reusability
Build components that can be reused across your application:
<!-- Reusable Button Component -->
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'default' | 'danger'
size?: 'small' | 'default' | 'large'
disabled?: boolean
loading?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'default',
size: 'default',
disabled: false,
loading: false
})
const emit = defineEmits<{
click: [event: MouseEvent]
}>()
</script>
<template>
<button
:class="[
'uk-button',
`uk-button-${variant}`,
`uk-button-${size}`
]"
:disabled="disabled || loading"
@click="emit('click', $event)"
>
<span v-if="loading" uk-spinner="ratio: 0.5"></span>
<slot v-else />
</button>
</template>
Composition
Combine smaller components to build complex UIs:
<template>
<UserForm>
<FormField label="Username" v-model="user.username" />
<FormField label="Email" v-model="user.email" type="email" />
<FormActions>
<Button variant="primary" @click="save">Save</Button>
<Button @click="cancel">Cancel</Button>
</FormActions>
</UserForm>
</template>
Composables
Extract and share logic across components:
// composables/useForm.ts
export function useForm(initialData, onSubmit) {
const form = ref({ ...initialData })
const errors = ref({})
const isSubmitting = ref(false)
async function submit() {
isSubmitting.value = true
try {
await onSubmit(form.value)
} catch (e) {
errors.value = e.response?.data?.errors || {}
} finally {
isSubmitting.value = false
}
}
return { form, errors, isSubmitting, submit }
}
UIkit Integration
UserFrosting uses UIkit for styling. Common UIkit components you'll use:
Buttons
<button class="uk-button uk-button-primary">Primary</button>
<button class="uk-button uk-button-default">Default</button>
<button class="uk-button uk-button-danger">Danger</button>
Cards
<div class="uk-card uk-card-default uk-card-body">
<h3 class="uk-card-title">Title</h3>
<p>Content</p>
</div>
Modals
<div id="my-modal" uk-modal>
<div class="uk-modal-dialog uk-modal-body">
<h2 class="uk-modal-title">Title</h2>
<p>Modal content</p>
</div>
</div>
Open programmatically:
import UIkit from 'uikit'
UIkit.modal('#my-modal').show()
Dropdowns
<button class="uk-button uk-button-default" type="button">
Menu
</button>
<div uk-dropdown>
<ul class="uk-nav uk-dropdown-nav">
<li><a href="#">Item 1</a></li>
<li><a href="#">Item 2</a></li>
</ul>
</div>
Best Practices
1. Component Organization
Organize components by feature or function:
components/
├── common/ # Reusable across app
│ ├── Button.vue
│ ├── Input.vue
│ └── Modal.vue
├── user/ # User-related
│ ├── UserCard.vue
│ ├── UserForm.vue
│ └── UserTable.vue
└── layout/ # Layout components
├── Header.vue
├── Sidebar.vue
└── Footer.vue
2. Props and Events
Follow Vue's data flow pattern:
- Props down: Parent passes data to child
- Events up: Child notifies parent of changes
<!-- Parent -->
<UserCard :user="user" @edit="handleEdit" @delete="handleDelete" />
<!-- Child -->
<script setup>
const props = defineProps(['user'])
const emit = defineEmits(['edit', 'delete'])
</script>
3. Type Safety
Use TypeScript for better developer experience:
interface User {
id: number
username: string
email: string
}
const props = defineProps<{
user: User
editable?: boolean
}>()
4. Error Handling
Always handle errors gracefully:
try {
await performAction()
showSuccess('Action completed')
} catch (error) {
showError('Action failed')
console.error(error)
}
What's Next?
Dive into specific component types:
- Forms - User input and validation
- Tables - Data display and manipulation
- Collections - Dynamic lists
- Alerts - User feedback
Or explore foundational concepts:
- Vue Components - Vue 3 basics
- Exporting Variables - Data passing
- Asset Management - Building and bundling