Telerik blogs

See how great Server Components are in Vue with Nuxt. They can also be used in conjunction with client components.

A Server Component is a component in your framework that does not load JavaScript. The server may use JavaScript to generate the code, but the output is pure HTML and CSS.

Today, Next.js is not the only server-side framework with Server Components. Nuxt 3 also has them, and they are even better in Vue than in React.

TL;DR

Nuxt Server Components are difficult to figure out, but after you understand them, they are simple. You can load pure HTML from the server for non-interactive components, and you can mix and match them together. All you need is to turn on the experimental tools. They are based on component islands, which are the building blocks for Server Components.

Experimental

Nuxt Standalone Server Components are still experimental today, and there are still a few missing features on the Roadmap. In any case, I recently built my first Nuxt App using Firebase, and I wanted to delve further. I admit I didn’t realize how awesome Nuxt would be.

Edit your nuxt.config.ts file so that you turn on component islands.

export default defineNuxtConfig({
  devtools: { enabled: true },
  experimental: {
    componentIslands: true
  },
  ...
});

Setup

First, we need to set up the client component for reference.

About Composable

Create the About Composable to get your data. Here I’m using a Firebase Document to display content from the database, but you could use any data you like. I also have Tailwind installed for easy CSS.

// composables/about.ts
import { doc, getDoc } from "firebase/firestore"

type AboutDoc = {
    name: string
    description: string
}

export const useAbout = async () => {

    // runs on both server and client

    const runtimeConfig = useRuntimeConfig()

    const { $db } = useNuxtApp()

    const aboutSnap = await getDoc(
        doc($db, '/about/ZlNJrKd6LcATycPRmBPA')
    )

    if (!aboutSnap.exists()) {
        throw 'Document does not exist!'
    }

    const data = aboutSnap.data()

    if (runtimeConfig.public.dev) {
        console.log(data)
    }

    return data as AboutDoc
}

About Component

Here we create an about component to seed the data. It will be reused for different kinds of components. We load the data in a component asynchronously.

// components/about.vue
<script setup lang="ts">

const data = await useAbout()

</script>

<template>
    <div class="flex items-center justify-center my-5">
        <div class="border w-[400px] p-5 flex flex-col gap-3">
            <h1 class="text-3xl font-semibold">{{ data.name }} - Island</h1>
            <p>{{ data.description }}</p>
        </div>
    </div>
</template>

About Client

Creating the client component is as simple as importing it as usual.

// pages/index.vue
<template>
    <About />
</template>

What Is This Island Thing?

Server Components use component islands under the hood. They are just non-interactive components that are rendered without any client JS. Think of them as the building blocks for Server Components.

Create the Island

Before we use server components, let’s build an island to see how that works.

First, create an island component inside the components/islands directory. All we do is import the data (without using import statements, I might add) into the component. It can be named anything as long as it is in that directory.

// components/islands/about-island.vue
<template>
    <About />
</template>

We then create a route inside our pages directory to display an island component.

// pages/island.vue
<template>
    <NuxtIsland name="AboutIsland" />
</template>

The core thing we will notice is that we are not hydrating any components in the browser. We are fetching the HTML only. This works, and the returned component is pure HTML without JavaScript.

Server Components

While NextJS has the app directory, Nuxt has the pages directory. Instead of manually creating the island components, we could create a server component by renaming a component to .server.vue at the end. This does the same thing and doesn’t require an islands directory.

// components/about-server.server.vue
<template>
    <About />
</template>

Notice the code for the component is the exact same, minus the text clarifier. The JavaScript in the component is not shipped to the browser.

Mixing Components

You can mix server and client components, but there are rules.

Counter

First, let’s create an interactive client component.

<script setup lang="ts">

const count = ref<number>(0)

function increment() {
    count.value++
}
</script>

<template>
    <div class="flex flex-col items-center">
        <p class="flex gap-3">
            <span class="font-semibold">
                Number:
            </span>
            {{ count }}
        </p>
        <button class="p-3 mt-3 border border-1 rounded-lg" @click="increment">
            Increment
        </button>
    </div>
</template>

Server Within Client Components

You can easily put an interactive component and a non-interactive component together by importing them alongside each other in a root component.

// pages/island.vue
<template>
    <NuxtIsland name="AboutIsland" />
    <Counter />
</template>

Here, we import a server component and a counter component next to each other on an About page. This works as expected, and interactivity is on the same page as a non-interactive component.

Client Within Server Component

You cannot put a client component directly in a server component, but you can indirectly by using slots. First, turn on experimental selective client.

// nuxt.config.ts
experimental: {
  componentIslands: {
    selectiveClient: true
  }
},

Next, make a slot in your server component.

// pages/about-server.server.vue
<template>
    <About />
    <slot />
</template>

And pass the client component through the slot.

<template>
    <AboutServer>
        <Counter />
    </AboutServer>
</template>

This was not evident at first but it works.

Now go rebuild all your Vue apps using Nuxt 3 and Server Components. I like Vue so much more than React. And Server Components make apps amazing.

Demo: Vercel

Repo: GitHub


Vue
About the Author

Jonathan Gamble

Jonathan Gamble has been an avid web programmer for more than 20 years. He has been building web applications as a hobby since he was 16 years old, and he received a post-bachelor’s in Computer Science from Oregon State. His real passions are language learning and playing rock piano, but he never gets away from coding. Read more from him at https://code.build/.

 

 

Related Posts

Comments

Comments are disabled in preview mode.