---
# System prepended metadata

title: Nuxt 3

---

# Nuxt 3
## :sparkles: Install
```terminal
npx nuxi@latest init project_name
pnpm dlx nuxi@latest init project_name
```

## :sparkles: Configuration
#### App config
```javascript
// app.config.ts
export default defineAppConfig({
  title: 'Hello Nuxt',
  theme: {
    dark: true,
    colors: {
      primary: '#ff0000'
    }
  }
})

// script setup
const appConfig = useAppConfig()
const title = appConfig.title // Hello Nuxt
```

#### Runtime config
```javascript
// nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    // The private keys which are only available server-side
    API_KEY: process.env.API_KEY,
    // Keys within public are also exposed client-side
    public: {
      API_BASE_URL: 'oh-my-god/api'
    }
  }
})

// script setup
const runtimeConfig = useRuntimeConfig();
runtimeConfig.public.API_BASE_URL
```

#### App config vs Runtime config
- **App config** is used for buildtime config and public information when **Runtime config** is used to expose env or sensitive infomation like private token 
- Env support only to **Runtime config**
- Client-side can access **App config** and public **Runtime config**, but can't access private **Runtime config**
- Server-side can access all

#### Environment overrides
```javascript
export default defineNuxtConfig({
  $production: {
    routeRules: {
      '/**': { isr: true }
    }
  },
  $development: {
    //
  }
})
```

#### Path alias
```javascript
alias: {
  "@utils": "../utils",
}
```

#### Enable experiment features
```javascript
// nuxt.config.js
export default defineNuxtConfig({
  vue: {
    defineModel: true,
    propsDestructure: true
  }
})
```

##### defineModel
```jsx
<script setup>
const modelValue = defineModel()
console.log(modelValue.value)
</script>

<template>
  <input v-model="modelValue" />
</template>
```

## :sparkles: Views
#### Nitro plugin
```javascript
// server/plugins/extend-html.ts
export default defineNitroPlugin((nitroApp) => {
  nitroApp.hooks.hook("render:html", (html, { event }) => {
    // This will be an object representation of the html template.
    console.log(html);
    html.head.push(
      `<meta name="description" content="My custom description" />`
    );
  });
  // You can also intercept the response here.
  nitroApp.hooks.hook("render:response", (response, { event }) => {
    console.log(response);
  });
});
```

## Assets
#### Public
Referencing an image file in the public/img/nuxt.png directory, available at the static URL /img/nuxt.png
```jsx
<template>
  <img src="/img/nuxt.png" alt="Discover Nuxt 3" />
</template>
```

#### Assets
Global style import
```javascript
export default defineNuxtConfig({
  vite: {
    css: {
      preprocessorOptions: {
        scss: {
          additionalData: '@use "@/assets/_colors.scss" as *;'
        }
      }
    }
  }
})
```

Local component style import
```sass
<style lang="scss" scoped>
@import "~/assets/test.scss";
@import "~/assets/css/test.css";
</style>
```

## Styling
#### Font
```css
/* main.css */
@font-face {
  font-family: 'Minecraft';
  src: url('~/fonts/Minecrafter.Alt.ttf') format('truetype');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}
```

#### Postcss
```javascript
// nuxt.config.ts
export default defineNuxtConfig({
  postcss: {
    plugins: {
      'postcss-autoprefixer': {}
    }
  }
})
```

```javascript
// plugins/postcss-autoprefixer.js
module.exports = {
  plugins: {
    autoprefixer: {}
  }
}
```

## Routing
#### Middleware
- kebab-case
- to make it global, use suffix .global: auth.global.ts

```javascript
// middleware/auth.global.ts
export default defineNuxtRouteMiddleware(() => {
  console.log('global middleware')
})
```

```javascript
// pages/test.vue
<script setup lang="ts">
definePageMeta({
  middleware: "local",
});
</script>
```

#### Route validation
- if validate false, redirect to 404 page
```javascript
definePageMeta({
  validate: async (route) => {
    // Check if the id is made up of digits
    return /^\d+$/.test('a');
  },
});
```

## Fetching Data
#### $fetch
- Built-in library ofetch
- Can use anywhere
- Using $fetch in components without wrapping it with useAsyncData causes fetching the data twice: initially on the server, then again on the client-side during hydration, because $fetch does not transfer state from the server to the client

#### useFetch
- Composable
- Avoid refetching the same data when the code is executed in the browser
- Shorthand of useAsyncData with $fetch

#### useAsyncData
- Composable
- Avoid refetching the same data when the code is executed in the browser
- More options than useFetch
```jsx
<script setup lang="ts">
// During SSR data is fetched twice, once on the server and once on the client.
const dataTwice = await $fetch('/api/item')

// During SSR data is fetched only on the server side and transferred to the client.
const { data } = await useAsyncData('item', () => $fetch('/api/item'))

// You can also useFetch as shortcut of useAsyncData + $fetch
const { data } = await useFetch('/api/item')
</script>
```

## State management
- useState is an SSR-friendly ref replacement. Its value will be preserved after server-side rendering (during client-side hydration) and shared across all components using a unique key
- To globally invalidate cached state, use clearNuxtState(id?)

```jsx
<script setup lang="ts">
const counter = useState('counter', () => Math.round(Math.random() * 1000))
// const counter = useCounter()
</script>

<template>
  <div>
    Counter: {{ counter }}
    <button @click="counter++">
      +
    </button>
    <button @click="counter--">
      -
    </button>
  </div>
</template>
```

## Error handling
#### During rendering lifecycle
- You can hook into Vue errors using onErrorCaptured. Or global propagate up by using vue:error
- For using an error reporting framework, you can provide a global handler through vueApp.config.errorHandler
```javascript
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.config.errorHandler = (error, context) => {
    // ...
  }
})
```

## Key concept
#### Auto import
- Components
```javascript
// change directory for auto import
export default defineNuxtConfig({
  components: {
    dirs: []
  }
})
```
- Composables and Utils
```javascript
// disable autoImport
export default defineNuxtConfig({
  imports: {
    autoImport: false
  }
})
```

- Built-in: fetch, useAsyncData (nuxt), ref, computed (Vue)
- Cannot use them outside a Nuxt plugin, Nuxt route middleware or Vue setup function
```javascript
const config = useRuntimeConfig()

export const useMyComposable = () => {
  // accessing runtime config here => error
}
```
```javascript
export const useMyComposable = () => {
  // Because your composable is called in the right place in the lifecycle => work
  const config = useRuntimeConfig()
}
```

- Third-party package
```javascript
export default defineNuxtConfig({
  imports: {
    presets: [
      {
        from: 'vue-i18n',
        imports: ['useI18n']
      }
    ]
  }
})
```

#### Components
- Custom auto import components
```javascript
export default defineNuxtConfig({
  components: [
    { path: '~/user-module/components', options },

    //
    path: '~/components',
+   extensions: ['.vue'],
    pathPrefix: false,
    prefix: false,
  ]
})
```

- Dynamic component
```jsx
<script setup lang="ts">
import { SomeComponent } from '#components'
// or
const MyButton = resolveComponent('MyButton')
</script>

<template>
  <component :is="clickable ? MyButton : 'div'" />
</template>
```

- Lazy-loading component
```jsx
<template>
  <div>
    <h1>Mountains</h1>
    <LazyMountainsList v-if="show" /> // component: MountainsList
    <button v-if="!show" @click="show = true">Show List</button>
  </div>
</template>
```

- Client-only component
```jsx
<template>
  <div>
    <Sidebar />
    <!-- This renders the "span" element on the server side -->
    <ClientOnly fallbackTag="span">
      <!-- this component will only be rendered on client side -->
      <Comments />
      <template #fallback>
        <!-- this will be rendered on server side -->
        <p>Loading comments...</p>
      </template>
    </ClientOnly>
  </div>
</template>
```
Or use suffix component name: Comments.client.vue

- Server-only component, Dev-only component