Components
- Single File Components (SFCs)
- Using Components in Other Components
- Component Communication Basics
- TypeScript in Vue
Vue 3 components are the building blocks of your application's interface. This page introduces the essential concepts you need to start building with Vue: Single File Components (SFCs) for structuring your code and component communication for passing data between parent and child components.
Single File Components (SFCs)
Vue uses Single File Components (SFCs) that bundle template, logic, and styles in one .vue file. Everything related to a component lives together, making it easy to understand and maintain.
A Vue component has three sections:
<template>- HTML structure and dynamic bindings<script setup>- Component logic and reactive state<style scoped>- Component-specific styles
1. Template (Required)
The <template> section defines your HTML structure. Use Vue's special syntax for dynamic content:
<template>
<!-- Display data -->
<p>{{ message }}</p>
<!-- Bind attributes -->
<img :src="imageUrl" :alt="imageAlt" />
<!-- Handle events -->
<button @click="handleClick">Click me</button>
<!-- Conditional rendering -->
<p v-if="isVisible">I'm visible!</p>
<!-- Loop through data -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</template>
2. Script Setup (Required)
The <script setup> section contains your component's logic. UserFrosting uses TypeScript (lang="ts") for better development experience:
<script setup lang="ts">
import { ref, computed } from 'vue'
// Everything here is automatically available in the template
const count = ref(0)
const doubled = computed(() => count.value * 2)
function increment() {
count.value++
}
</script>
Note
UserFrosting uses Vue 3's <script setup> syntax, which is more concise than the older Options API. Everything defined in this block is automatically available in your template—no need to explicitly return or export.
Tip
By default <script setup lang="ts"> is used in UserFrosting to enabled Typescript in components for better type safety and editor support. You can use plain JavaScript by omitting lang="ts", but we recommend TypeScript for a better development experience.
3. Style (Optional)
The <style scoped> section contains CSS that only applies to this component:
<style scoped>
.my-button {
background: blue;
color: white;
}
/* This won't affect other components' .my-button classes */
</style>
The scoped attribute is important—it prevents your styles from accidentally affecting other components. Always use it unless you explicitly want global styles. Learn more about scoped CSS in Vue's documentation.
Tip
UserFrosting rarely makes uses the style section, as we prefer to keep styles in separate LESS files. However, for small, component-specific styles, this is a convenient option.
Using Components in Other Components
One of Vue's most powerful features is component composition—the ability to build larger interfaces by nesting smaller, reusable components. Any component can import and use other components in its template, creating a tree-like hierarchy.
Here's a simple counter button component:
<!-- ButtonCounter.vue -->
<script setup lang="ts">
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">
You clicked me {{ count }} times.
</button>
</template>
To use this component in another component, simply import it and include it in your template:
<!-- App.vue -->
<script setup lang="ts">
import ButtonCounter from './ButtonCounter.vue'
</script>
<template>
<h1>Here is a child component!</h1>
<ButtonCounter />
<ButtonCounter />
<ButtonCounter />
</template>
Test this example on Vue Playground.
Each <ButtonCounter /> instance maintains its own separate state. Each button will maintain its own, separate count—that's because a new instance of the component is created each time you use it.
Note
In SFCs, it's recommended to use <PascalCase /> tag names for child components to differentiate from native HTML elements. PascalCase names are more consistent with JavaScript conventions.
Tip
For more details on component composition, props, events, and advanced patterns, visit the Vue 3 Component Basics documentation.
Component Communication Basics
Components need to communicate with each other. Vue uses two main patterns:
Props: Parent → Child
Parents pass data down to children via props:
<!-- Parent.vue -->
<template>
<UserCard username="Alex" :message-count="5" />
</template>
<!-- UserCard.vue -->
<script setup lang="ts">
const props = defineProps<{
username: string
messageCount: number
}>()
</script>
<template>
<p>{{ username }} has {{ messageCount }} messages</p>
</template>
Emits: Child → Parent
Children notify parents of events via emits:
<!-- Child.vue -->
<script setup lang="ts">
const emit = defineEmits<{
deleteClicked: [userId: number]
}>()
function handleDelete() {
emit('deleteClicked', 123)
}
</script>
<template>
<button @click="handleDelete">Delete</button>
</template>
<!-- Parent.vue -->
<template>
<UserCard @delete-clicked="handleUserDeleted" />
</template>
<script setup lang="ts">
function handleUserDeleted(userId: number) {
console.log('Delete user:', userId)
}
</script>
Important
Always follow unidirectional data flow: props down, events up. Never mutate props directly in a child component—emit an event to let the parent handle it.
TypeScript in Vue
To use TypeScript in Vue components, simply add lang="ts" to your <script setup> block. This enables type checking and better editor support for your component's logic.
Basic TypeScript usage in components:
<script setup lang="ts">
import { ref, Ref } from 'vue'
// Define interfaces for complex types
interface User {
id: number
username: string
email: string
}
// Type your props
const props = defineProps<{
user: User
}>()
// Type your refs
const count: Ref<number> = ref(0)
const users: Ref<User[]> = ref([])
// Type your functions
function selectUser(user: User): void {
console.log('Selected:', user.username)
}
</script>
Don't worry if TypeScript feels unfamiliar-you can start with basic types and gradually learn more as you go. For deeper language concepts, continue with the official TypeScript docs.