Create a blog in Nuxt 3

It has been 3 years since I built this blog in Nuxt 2 but the support of Nuxt 2 will end soon, so I created it again in Nuxt 3.

Initialize the project

Refer to the following page to initialize the project using Nuxt Content v2.

Reference: Installation - Nuxt Content

Environment variables

Nuxt 3 has dotenv built in.

Reference: .env · Nuxt Directory Structure

If you prepare several files, such as .env.local, .env.production, etc., you can set the environment variables for each environment. If you are connecting from the same local network, set the following variables:

.env.local

HOST=0
PORT=3000

Preprocessors

Pug.js

terminal

$ yarn add pug

You can use Pug.js by setting pug to lang attribute of <template>.

Sass

Reference: # Using Preprocessors

Modules

Nuxt 3 offers additional functionality as a module. I think it covers most of the functions you need.

Nuxt UI

Bulma and Buefy are not provided for Nuxt 3, so I use Nuxt UI (@nuxt/ui) as an alternately.

Nuxt UI is based on Tailwind CSS. @nuxtjs/color-mode and nuxt-icon are imported automatically, but it is necessary to edit the config file to use them.

nuxt.config.ts

export default defineNuxtConfig({
  modules: ['@nuxt/ui', '@nuxtjs/color-mode', 'nuxt-icon'],
})

If you would like to change the default color, choose from the provided color map or create a new one. The following site is very helpful in creating a color map.

Reference: uicolors.app/create

Running the following command will create the config file in the root directory.

terminal

$ npx tailwindcss init --ts

Reference: Using ESM or TypeScript

The color map referenced by components is set in app.config.ts.

Reference: Theming - Nuxt UI

Nuxt UI Pro provides many components, but you must pay to use them in production.

Dark mode

It is enabled by the settings above.

In @nuxtjs/color-mode, each class name is light-mode and dark-mode. In @nuxt/ui, they are light and dark.

Icon

You can easily reference different icon packages using <Icon> tag.

Reference: GitHub - nuxt-modules/icon: The <Icon> component, supporting Iconify, Emojis and custom components.

You would like to use SVG icons, you can also use nuxt-icons package. Its name is similar, but it is a different package.

Reference: Icons · Nuxt Modules

Syntax Highlighting

Nuxt 3 uses Shikiji for syntax highlighting by default.

Reference: highlight

This highlight module addresses light/dark modes, but this setting is overridden by default settings.

In Nuxt Content v1, the highlight module displays a file name, but it is not available in Shikiji. If you would like to display the file name, you need to overwrite the component.

Reference: How to Create a Custom Code Block With Nuxt Content v2 | Michael Hoffmann - Senior Frontend Developer (Freelancer)

Generate Open Graph image

Og-image module automatically generates the image for Open Graph.

Reference: Og-image · Nuxt Modules

It is necessary to specify the font style if you use non-English language.

Reference: Non-English Locales · Nuxt SEO

You can use HTML tags to make the image, but the image design will be different from the appearance of the HTML page because some tags are not supported. I think linebreaks won't work.

Sitemap

Reference: Sitemap · Nuxt Modules

Google Analytics

Reference: Gtag · Nuxt Modules

Internationalization

Reference: GitHub - nuxt-modules/i18n: I18n module for Nuxt

Edit the project

Directory Structure

Reference: Nuxt Guide

layouts

It is necessary to edit app.vue.

Reference: layouts/ · Nuxt Directory Structure

The difference between composables and utils

Composable is available in Nuxt 3 using Vue's Composition API. The Composition API encapsulates the state control logic for reusability.

Reference: Composables | Vue.js

The scripts in the composables and utils directories are automatically imported. The encapsulated processes that are not composables are stored in utils.

Reference: utils/ · Nuxt Directory Structure

script setup

<script setup> tag is available as of Vue 3. Its way of writing is different from the previous version.

<script> はコンポーネントがインポートされた最初のタイミングで実行されますが,<script setup> はコンポーネントのインスタンスが作成されるたびに実行されます.

The difference is below:

  • <script>: run when the component is first imported
  • <script setup>: run when the instance of the component is created

Reference: <script setup> | Vue.js

A component looks like this:

<template>
  <div>
    <p v-if="hasName">{{ name }}</p>
  </div>
</template>

<script setup>
const props = defineProps({
  name: {
    type: String,
    default: '',
  },
})

const { name } = props

const hasName = computed({
  get() {
    return name === 'alice'
  },
})
</script>

Reference: Writable Computed

Get content from Markdown file

$content tag in Nuxt Content v1 is unavailable and alternatively available for useAsyncData() function.

<script setup>
const { data } = await useAsyncData('home', () => queryContent('/').findOne())
</script>

Note that the retrieved data is controlled by the first argument, so the other argument should be specified when retrieving data in parallel.

Reference: queryContent() - Nuxt Content

Execution environment

nuxt dev or preview

In Nuxt 3, nuxt preview is available. This command works the same as start.

Reference: nuxi preview · Nuxt Commands

Connect from the local network

This script makes it available to connect from the same local network.

package.json

{
  "scripts": {
    "local": "yarn dev --dotenv .env.local"
  }
}

terminal

$ yarn local

SEO

favicon

According to Google's guidelines, it is necessary to prepare icons in the size multiple of 48px.

Reference: Define Website Favicon for Search Results | Google Search Central  |  Documentation  |  Google for Developers

But some icons are required for iPhone/iPad(Pro/mini)/Mac by Apple's guide.

Reference: App icons | Apple Developer Documentation

Therefore, I suggest using SVG because there is no need to specify the size. Apple-touch-icon is also required for the device of iOS/iPad OS. Its size is 180x180.

Reference: useSeoMeta

Conclusion

The Lighthouse score of the previous blog is below:

Mobile

91Performance
83Accessibility
95Best Practices
90SEO
0-49
50-89
90-100

Reported by Lighthouse

PC

99Performance
83Accessibility
100Best Practices
90SEO
0-49
50-89
90-100

Reported by Lighthouse

After rebuilding with Nuxt 3, this score decreased slightly due to the size of the JS library and the implementation of AdSense.

Mobile

85Performance
81Accessibility
100Best Practices
87SEO
0-49
50-89
90-100

Reported by Lighthouse

PC

90Performance
81Accessibility
100Best Practices
90SEO
0-49
50-89
90-100

Reported by Lighthouse