404 Not Found

404 Not Found


nginx

Components and Layouts

Charlie's team consists of five developers, and each one writes components differently—some import them manually, others register them globally, and there's no consistent naming convention. Alice noticed that the page was loading a lot of unnecessary components, which was slowing down the first-screen load. Bob wanted to use a different layout for the admin dashboard but didn't know how to switch to it.

1. What You'll Learn


2. A True Story About a Team

(1) Pain Point: Disorganized Component Management

Charlie's team of five works in silos: Alice manually imports each component, Bob uses global registration, which causes the package size to balloon, and the new colleague can't figure out what the components are called. There are three different ways to reference a single ProductCard across different files.

(2) Solution for Nuxt 3's Automatic Import

Nuxt 3 automatically scans the components/ directory, generates component names according to the rules, and ensures everyone references them in a consistent manner:

VUE
<!-- No import needed, just use it -->
<ProductCard :product="item" />

(3) Benefits: Standardization + Reduced Size

The team standardized the way components are referenced; the "Lazy" prefix ensures that only visible components are loaded on the first screen, reducing the size of MegaShop's first-screen JavaScript by 40%.


3. Automatic Component Import

(1) Directory Scanning and Naming Conventions

100%
graph TB
    A[components/] --> B[Root level]
    A --> C[Sub-directory]
    A --> D[Nested directory]
    B --> E[ProductCard.vue → ProductCard]
    C --> F[product/Card.vue → ProductCard ①]
    D --> G[shop/product/Card.vue → ShopProductCard]
    style G fill:#ffe,stroke:#f90
⚠️ Note: ① After configuring pathPrefix: false, product/Card.vueCard may result in a naming conflict. We recommend keeping the default prefix.

(2) Comparison of Naming Conventions

File Path Default Component Name pathPrefix: false
components/AppHeader.vue AppHeader AppHeader
components/product/Card.vue ProductCard Card
components/shop/ProductCard.vue ShopProductCard ProductCard
components/admin/user/Table.vue AdminUserTable Table

(1) ▶ Example: Automatic Component Import

VUE
<!-- components/ProductCard.vue -->
<template>
  <div class="product-card">
    <img :src="product.image" :alt="product.name" />
    <h3>{{ product.name }}</h3>
    <p>${{ product.price }} USD</p>
    <button @click="$emit('add-to-cart', product)">Add to Cart</button>
  </div>
</template>

<script setup lang="ts">
interface Product {
  id: number
  name: string
  price: number
  image: string
}

defineProps<{ product: Product }>()
defineEmits<{ 'add-to-cart': [product: Product] }>()
</script>

Output:

TEXT
// Execution Successful

(2) ▶ Example: Using the Auto-Import Component on a Page

VUE
<!-- pages/products/index.vue -->
<template>
  <div>
    <h1>All Products</h1>
    <div class="grid">
      <!-- ProductCard auto-imported -->
      <ProductCard
        v-for="p in products"
        :key="p.id"
        :product="p"
        @add-to-cart="handleAdd"
      />
    </div>
  </div>
</template>

<script setup lang="ts">
const { data: products } = await useFetch('/api/products')
function handleAdd(product: any) {
  console.log('Added:', product.name)
}
</script>

Output:

TEXT
// Execution Successful

4. Layouts Layout System

(1) How Layout Works

100%
graph TB
    A[app.vue] --> B[NuxtLayout]
    B --> C{Current Layout}
    C -->|default| D[layouts/default.vue]
    C -->|sidebar| E[layouts/sidebar.vue]
    D --> F[NuxtPage - Page Content]
    E --> F

(2) The Relationship Between app.vue and Layout

VUE
<!-- app.vue - Root component with layout support -->
<template>
  <NuxtLayout>
    <NuxtPage />
  </NuxtLayout>
</template>

(1) ▶ Example: Default Layout

VUE
<!-- layouts/default.vue -->
<template>
  <div class="layout-default">
    <AppHeader />
    <main class="content">
      <!-- Page content renders here -->
      <slot />
    </main>
    <AppFooter />
  </div>
</template>

Output:

TEXT
// Execution Successful

(2) ▶ Example: Sidebar Layout

VUE
<!-- layouts/sidebar.vue -->
<template>
  <div class="layout-sidebar">
    <AppHeader />
    <div class="main-wrapper">
      <aside class="sidebar">
        <nav>
          <NuxtLink to="/admin">Dashboard</NuxtLink>
          <NuxtLink to="/admin/products">Products</NuxtLink>
          <NuxtLink to="/admin/orders">Orders</NuxtLink>
        </nav>
      </aside>
      <main class="content">
        <slot />
      </main>
    </div>
  </div>
</template>

Output:

TEXT
// Execution Successful

(3) ▶ Example: Page-Switching Layout

VUE
<!-- pages/admin/index.vue -->
<template>
  <div>
    <h1>Admin Dashboard</h1>
    <p>Welcome, Bob</p>
  </div>
</template>

<script setup lang="ts">
// Switch to sidebar layout for this page
definePageMeta({
  layout: 'sidebar'
})
</script>

Output:

TEXT
// Execution Successful

(3) Comparison of Layout vs. Nested Routes

Dimension Layout Nested Routing
Definition Location layouts/*.vue pages/parent.vue
Content Slot <slot /> <NuxtPage />
Switching Method definePageMeta Automatic (Routing Hierarchy)
Data Retrieval Not Recommended Each Gets What They Need
Use Cases Overall Page Layout Shared UI Across Routing Levels

5. Lazy Loading of Components

(1) Lazy Loading Mechanism

Nuxt 3 automatically registers a "Lazy" version of each component, loading the code only when the component is rendered for the first time.

(1) ▶ Example: Lazy prefix lazy loading

VUE
<template>
  <div>
    <!-- Eagerly loaded - included in initial bundle -->
    <AppHeader />

    <!-- Lazy loaded - code-split, loaded on first render -->
    <LazyProductCard
      v-for="p in products"
      :key="p.id"
      :product="p"
    />

    <!-- Lazy with v-if - only loaded when condition is true -->
    <LazyProductReviewModal
      v-if="showReviewModal"
      :product-id="selectedProductId"
      @close="showReviewModal = false"
    />
  </div>
</template>

Output:

TEXT
// Execution Successful

(2) ▶ Example: Asynchronous Loading with Suspense

VUE
<template>
  <div>
    <Suspense>
      <template #default>
        <LazyHeavyChart :data="chartData" />
      </template>
      <template #fallback>
        <div class="loading">Loading chart...</div>
      </template>
    </Suspense>
  </div>
</template>

Output:

TEXT
// Execution Successful

(2) Comparison of Lazy-Loading Strategies

Strategy Implementation Loading Timing Applicable Scenarios
Instant Load <ProductCard /> When the page loads Core components on the first screen
Lazy prefix <LazyProductCard /> On first render Components below the fold
v-if + Lazy <LazyModal v-if="show" /> When the condition is true Pop-up/Drawer
Suspense <Suspense> Package When asynchronous operation is ready A "loading" state is required

6. Best Practices for Props and Emits

(1) ▶ Example: Type-Safe Props and Emits

VUE
<!-- components/ProductCard.vue -->
<script setup lang="ts">
interface Product {
  id: number
  name: string
  price: number
  image: string
  inStock: boolean
}

// Type-safe props with default values
const props = withDefaults(
  defineProps<{
    product: Product
    showStock?: boolean
    currency?: string
  }>(),
  {
    showStock: true,
    currency: 'USD'
  }
)

// Type-safe emits
const emit = defineEmits<{
  'add-to-cart': [product: Product]
  'toggle-wishlist': [productId: number]
}>()
</script>

<template>
  <div class="card">
    <img :src="product.image" :alt="product.name" />
    <h3>{{ product.name }}</h3>
    <p>{{ product.price }} {{ currency }}</p>
    <p v-if="showStock">
      {{ product.inStock ? 'In Stock' : 'Out of Stock' }}
    </p>
    <button @click="emit('add-to-cart', product)">Add to Cart</button>
    <button @click="emit('toggle-wishlist', product.id)">Wishlist</button>
  </div>
</template>

Output:

TEXT
// Execution Successful

7. Comprehensive Example: MegaShop Layout Component System

VUE
<!-- components/AppHeader.vue -->
<template>
  <header class="app-header">
    <NuxtLink to="/" class="logo">MegaShop</NuxtLink>
    <nav>
      <NuxtLink to="/products">Products</NuxtLink>
      <NuxtLink to="/categories">Categories</NuxtLink>
    </nav>
    <div class="user-area">
      <NuxtLink to="/cart">Cart ({{ cartCount }})</NuxtLink>
      <NuxtLink v-if="isLoggedIn" to="/admin">Admin</NuxtLink>
      <NuxtLink v-else to="/login">Sign In</NuxtLink>
    </div>
  </header>
</template>

<script setup lang="ts">
const cart = useState<any[]>('cart', () => [])
const cartCount = computed(() => cart.value.length)
const isLoggedIn = useState('isLoggedIn', () => false)
</script>
VUE
<!-- components/AppFooter.vue -->
<template>
  <footer class="app-footer">
    <p>&copy; 2026 MegaShop. All rights reserved.</p>
    <nav>
      <NuxtLink to="/about">About</NuxtLink>
      <NuxtLink to="/contact">Contact</NuxtLink>
      <NuxtLink to="/privacy">Privacy Policy</NuxtLink>
    </nav>
  </footer>
</template>
VUE
<!-- layouts/default.vue -->
<template>
  <div class="layout-default">
    <AppHeader />
    <main>
      <slot />
    </main>
    <AppFooter />
  </div>
</template>

❓ FAQ

Q Why isn't my component being automatically imported?
A Check whether the file is located in the components/ directory, whether the filename starts with a capital letter, and whether the path has been excluded from the components configuration in nuxt.config.ts. Try restarting the dev server.
Q Does the Lazy prefix cause the component to render later?
A No. Lazy only delays the loading of the component's JavaScript code; once the component renders, it works normally. It performs code splitting, not deferred rendering.
Q Can Layout retrieve data?
A Technically, yes, but it is not recommended. Layout is a UI framework; data retrieval should be handled within the page or a Composable. Layout is solely responsible for the layout structure.
Q Can a single page use multiple layouts?
A No. Each page can specify only one layout using definePageMeta. If you need different layouts for different sections, achieve this by combining components.
Q Does definePageMeta have to be at the top level of the script setup?
A Yes. definePageMeta is a compiler macro and must be called directly at the top level of <script setup>; it cannot be placed inside conditional statements or functions.
Q Does placing components in a subdirectory that's too deep under components/ affect performance?
A It does not affect runtime performance, because Nuxt 3 only bundles components that are actually used. However, a directory hierarchy that's too deep can increase naming complexity; we recommend limiting it to no more than two levels.

📖 Summary


📝 Exercises

  1. Basic Exercise (Difficulty: ⭐): Create the AppHeader and AppFooter components and use them in the default layout.
  2. Advanced Exercise (Difficulty: ⭐⭐): Create a sidebar layout and toggle between the two layouts on the admin page using definePageMeta. Compare the results of the two layouts.
  3. Challenge (Difficulty: ⭐⭐⭐): Use the Lazy prefix and v-if to implement a product details popup component that loads only when clicked, and use Suspense to display the loading state.

---|

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏