Learn Nuxt - Components & Layouts
Series/Learn Nuxt/Episode 5
Episode 5 of 24

Learn Nuxt - Components & Layouts

This episode covers reusable components with Single File Components, layouts for shared page scaffolding, nested layouts, slots for flexible component composition, and styling with CSS Modules, Tailwind, and component-scoped styles.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

A good page is built from small, reusable pieces. Episode 5 focuses on the two main tools for that: components for reusable UI elements, and layouts for page scaffolding shared across pages.

The problems we're solving: the header and footer should not be rewritten on every page, a product card should be a single component used in many places, and styles must be isolated so they don't override each other. After this episode, you will understand the Vue composition patterns that are the foundation of large-scale applications.

Reusable Components and SFC

Creating Components in the Components Folder

The app/components folder is dedicated to reusable components, and they are all auto-imported. Let's create a product card component:

HTMLapp/components/KartuProduk.vue
<script setup lang="ts">
defineProps<{
  nama: string
  harga: number
}>()
</script>
 
<template>
  <article class="kartu">
    <h2>{{ nama }}</h2>
    <p>Rp {{ harga }}</p>
  </article>
</template>

defineProps<{ nama: string }>() declares the props the component accepts with full type checking. This component can be used on any page right away, no import needed.

Component Naming Rules

Use component names of two words or more, for example KartuProduk, not Kartu. A single-word name risks colliding with native HTML elements. Nuxt also understands nested folder structures — components/kartu/Header.vue can be used as <KartuHeader />.

Layouts and Nested Layouts

Page Scaffolding with Layouts

A layout is a scaffold that wraps page content. The default layout holds a header, <NuxtPage />, and a footer:

HTMLapp/layouts/default.vue
<template>
  <div>
    <header>Header Toko</header>
    <main>
      <NuxtPage />
    </main>
    <footer>Footer Toko</footer>
  </div>
</template>

<NuxtPage /> renders the active page inside the layout. Every page that doesn't specify another layout automatically uses default.

Choosing a Layout Per Page

Create a second layout, e.g. app/layouts/clean.vue, then register it on a specific page:

HTMLHalaman dengan layout custom
<script setup lang="ts">
definePageMeta({ layout: "clean" })
</script>

definePageMeta({ layout: "clean" }) makes that page render with the clean layout. For nested layouts — a layout inside a layout — simply wrap <NuxtPage /> with another layout layer by layer.

Slots and Component Composition

Default Slots

Slots allow a component to accept content from outside. The KotakInfo component can be used with different content each time:

HTMLapp/components/KotakInfo.vue
<template>
  <div class="kotak-info">
    <slot />
  </div>
</template>
HTMLPemakaian slot default
<KotakInfo>
  <p>Promo gratis ongkir minggu ini.</p>
</KotakInfo>

<slot /> is the insertion point for the component's child content. This pattern makes components flexible without needing endless props.

Named Slots and Slot Props

For multiple areas within a single component, use named slots. Slots can also send data back to the consumer through slot props. You'll use the full details when building form components in episode 10.

Styling with CSS Modules, Tailwind, and Scoped Styles

Scoped Styles

The simplest and safest approach: add the scoped attribute so the CSS only applies to that component:

HTMLScoped style
<style scoped>
.kartu {
  border: 1px solid #ddd;
  padding: 1rem;
}
</style>

scoped makes Nuxt add a unique attribute to the component's elements and write matching CSS selectors, so h2 in this component does not affect h2 elsewhere.

CSS Modules

For more explicit styling, enable CSS Modules:

HTMLCSS Modules
<style module>
.kartu {
  background: #f5f5f5;
}
</style>
HTMLPakai CSS Modules
<template>
  <article :class="$style.kartu">
    Konten kartu
  </article>
</template>

With module, classes are accessed through $style.kartu and hashed so they don't collide across components.

Tailwind CSS

For utility-first styling, install the Tailwind module:

Install Tailwind untuk Nuxt
npm install --save-dev @tailwindcss/vite
JSAktifkan Tailwind
import tailwindcss from "@tailwindcss/vite"
 
export default defineNuxtConfig({
  vite: {
    plugins: [tailwindcss()],
  },
})

After that you can write utility classes directly in the template, for example class="border p-4". The choice between scoped styles, CSS Modules, or Tailwind should stay consistent within one project.

Conclusion

Episode 5 gives you a complete set of UI composition tools: reusable components with auto-import and props, layouts for shared page scaffolding, slots for flexibility, and three styling approaches you can pick from based on project needs.

Key takeaways:

  • Components in app/components are auto-imported and should be named with at least two words.
  • Layouts wrap pages; default.vue is used by all pages automatically.
  • definePageMeta({ layout: "nama" }) switches a page to another layout.
  • Default and named slots make components composable from the outside.
  • scoped in <style> isolates CSS per component.
  • CSS Modules use $style and Tailwind is added through a Vite plugin.

In the next episode, episode 6, we will discuss data fetching and server actions — fetching data with useAsyncData and useFetch, writing server actions for forms, understanding the difference between server and client fetches, and caching and revalidating data. Your store will start consuming real data.

Learn Nuxt - Components & Layouts | Learn Nuxt