Getting Started with Astro for Vue Developers

Astro is a new content focused website builder that focuses on shipping less JavaScript. In this article, we will discuss how Vue.js developers can get started with Astro.

Introduction

Astro is a website builder that’s specifically tailored towards creating content-rich websites. It’s perfect for building a wide range of sites, such as marketing sites, publishing sites, documentation sites, blogs, portfolios, and even some e-commerce sites.

On the other hand, most modern web frameworks are designed to create web applications. These frameworks are ideal for building complex, application-like experiences within the browser.

It’s important to note the key difference between Astro and other web frameworks: Astro’s primary focus is on delivering exceptional content experiences. As a result, Astro can make specific tradeoffs and deliver unmatched performance features that wouldn’t be feasible with application-focused web frameworks.


Partial Hydration in Astro

Astro takes a different approach to rendering compared to other modern JavaScript web frameworks. It favors server-side rendering over client-side rendering as much as possible, which is the same approach used by traditional server-side frameworks like PHP, WordPress, Laravel, Ruby on Rails, and more. However, you don’t need to learn a second server-side language to use Astro because everything is still in HTML, CSS, and JavaScript (or TypeScript).

In contrast, frameworks like Next.js, SvelteKit, Nuxt, Remix, and others require client-side rendering of the entire website, with server-side rendering mainly used to address performance concerns. This approach is known as the Single Page App (SPA) model, which has its benefits but comes with additional complexity and performance tradeoffs. These tradeoffs can harm page performance, which is not ideal for content-focused websites where first-load performance is critical.

What makes Astro unique is its island component architecture and partial hydration rendering strategy. With partial hydration, developers can selectively hydrate parts of a page with JavaScript, reducing the initial load time and improving the overall performance of the site.

Astro can deliver fast and dynamic websites while still being SEO-friendly and accessible. This is a significant advantage over other frameworks that focus more on client-side rendering.


Getting Started with an Astro project

To get started with Astro, you will need to have Node.js (>=v16.0) and a package manager (NPM) installed on your machine.

Once you have installed Node.js and NPM, you can start a new Astro project using NPM, by running the following command in your terminal:

# npm
npx create-astro your-project

# pnpm
pnpx create-astro your-project

# yarn
yarn create astro your-project

The Astro create command will guide you through some initial setup:

  1. What template you’d like to use
    • Let’s choose empty for now
  2. If you’d like to install dependencies
  3. Are you planning to use TypeScript
    • What TypeScript settings you’d like to use
  4. Setup a new git repository

After these steps, you should have a new directory called your-project in the directory in which you ran the command. Once the project is created, you can navigate to the project directory by running the following command:

cd your-project

Here you can open your project in your favorite IDE and start the development server for the first time:

npm run dev

This command will start the development server and open your default web browser to http://localhost:3000, where you can see your Astro site. Astro is built upon Vite.js, which means you’ll get all of the awesome features that Vite offers, for example HMR.


Astro Integrations

Astro integrations are an excellent way to enhance the functionality and behavior of your project with minimal effort. You have the flexibility to create a custom integration yourself, utilize an official integration, or even make use of integrations built by the community.

Integrations can open up new possibilities, such as unlocking popular UI frameworks like React, Vue, Svelte, and Solid. With just a few lines of code, you can also integrate tools like Tailwind or Partytown into your project.

These integrations can add new features to your project, such as automatic sitemap generation, which can be hugely beneficial for SEO purposes. Additionally, you can write custom code that hooks into the build process, dev server, and other essential elements of your project.

Overall, Astro integrations are an effective way to add powerful new capabilities to your project while reducing the time and effort needed to do so. Whether you’re looking to improve your site’s performance, enhance its functionality, or streamline its development process, Astro integrations have got you covered.

Astro is built with extendability in mind. This way, anyone can write plug-ins to extend Astro.


Comparison between Nuxt and Astro

Nuxt and Astro are modern Javascript static-site generators that share some similarities. This makes it easier for developers to migrate their projects between the two. Both use an src/ folder for project files, and a special src/pages/ folder for file-based routing. This structure will be familiar to developers who have worked with Nuxt before.

Astro has an official integration for using Vue components and supports installing NPM packages, including several for Vue. This means developers can write Vue UI components and use existing components and dependencies. Both Nuxt and Astro allow you to use a headless CMS, APIs, or Markdown files for data, so you can continue to use your preferred content-authoring system.

However, there are also key differences between Nuxt and Astro. Nuxt is a Vue-based single-page application (SPA), while Astro sites are multi-page apps built using .astro components. Astro supports React, Preact, Vue.js, Svelte, SolidJS, AlpineJS, Lit and raw HTML templating. Nuxt uses vue-router for SPA routing and vue-meta for managing <head>, whereas in Astro, you create separate HTML page routes and control your page <head> directly, or in a layout component.

Astro was designed to excel at making content-focused websites. An existing Nuxt app might be built for high client-side interactivity and may include items that are difficult to replicate in Astro, such as dashboards. Thus, developers need to carefully consider the strengths of both Nuxt and Astro before making a decision on which one to use for their project.


Writing your first Astro page

Before we can start using Vue in Astro, you’ll first have to understand the Astro syntax, so let’s add some code into the index.astro file.

src/pages/index.astro

---
// This is Astro's frontmatter
// here you can write regular JavaScript or TypeScript
// It will only run on the server and never executes client-side

const title = "Hello World!";
const food = ["Pizza", "French fries", "Spaghetti"];
---
<html>
  <head>
    <!-- Astro uses a JSX-like syntax in it's body -->
    <title>{title}</title>
  </head>
  <body>
    <main>
      <h1>Food I like:</h1>
      <!-- let's render our food items -->
      <ul>
        { food.map(item => (
          <li>{item}</li>
        ))}
      <ul>
    </main>
  </body>
</html>

.astro components can also be imported on pages, other components, contain slots, or anything you’re used to in popular JavaScript frameworks.


Using Vue in Astro

As mentioned, Astro has integrations for other JavaScript frameworks, let’s take a look at actually using Vue components in Astro.

Install & setup

To start writing UI components in any framework, you’ll have to add the integration into Astro.

Astro has this amazing CLI tool called astro add which you can use to add any integration. The tool will also update your astro.config.mjs, so you don’t have to manually update it.

npx astro add vue

The CLI will show you something like:

✔ Resolving packages...

  Astro will run the following command:
  If you skip this step, you can always run it yourself later

 ╭─────────────────────────────────────────────────╮
 │ npm i @astrojs/vue astro@^2.1.0 vue@^3.2.30     │
 ╰─────────────────────────────────────────────────╯

It will then install the packages and update your astro.config.mjs to something like:

astro.config.mjs

import { defineConfig } from 'astro/config';
import vue from "@astrojs/vue";

// <https://astro.build/config>
export default defineConfig({
  integrations: [vue()]
});

Now that we have our Vue integration ready with Astro, we can start building UI components in Vue and use them on our site.

Let’s start out by adding a simple HelloWorld.vue component in the components directory.

src/components/food.vue

<template>
  <div>
    <ul>
      <li v-for="item in food">
        {{ item }}
      </li>
    </ul>
  </div>
</template>

<script setup lang="ts">
const food = ["Pizza", "French fries", "Spaghetti"];
</script>

This will replace our food list in the index.astro page.

Let’s get that component we just built on our website!

src/pages/index.astro

---
// This is Astro's frontmatter
// here you can write regular TypeScript or JavaScript
// It will only run on the server and never executes client-side
import Food from "../components/food.vue";

const title = "Hello World!";
---
<html>
  <head>
    <!-- Astro uses a JSX-like syntax in it's body -->
    <title>{title}</title>
  </head>
  <body>
    <main>
      <h1>Food I like:</h1>
      <!-- Rendering our food items with the Vue component -->
      <Food />
    </main>
  </body>
</html>

This code imports your food.vue component and renders it in your Astro site. If you now take a look at the running dev server, you should see exactly the same result as before, just using the Vue syntax.


Using Vue as you normally would

The previous was just a simple use case. The site you have in mind is probably more complicated.

So let’s take a look at a more sophisticated example that simulates a mobile menu button:

src/components/navigation.vue

<template>
  <div>
    <nav>
      <button role="button" @click="openMenu(true)" v-if="!isMenuOpen">
        Open Menu
      </button>
      <button role="button" @click="openMenu(false)" v-else>
        Close Menu
      </button>
      <ul v-if="isMenuOpen">
        <li>
          <a href="/">Home</a>
        </li>
        <li>
          <a href="/about">About</a>
        </li>
      </ul>
    </nav>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue';

const isMenuOpen = ref<boolean>(false)

const openMenu = (action: boolean) => {
  isMenuOpen.value = action;
}
</script>

The code above, is just a simple showcase of using ref() in Vue and Astro where we use a variable isMenuOpen to dynamically render our markup.

Next, let’s import this component into the index.astro file so it can appear in our webpage.

src/pages/index.astro

---
// This is Astro's frontmatter
// here you can write regular TypeScript or JavaScript
// It will only run on the server and never executes client-side
import Food from "../components/food.vue";
import Navigation from "../components/navigation.vue";

const title = "Hello World!";
---
<html>
  <head>
    <!-- Astro uses a JSX-like syntax in it's body -->
    <title>{title}</title>
  </head>
  <body>
    <header>
      <Navigation />
    </header>
    <main>
      <h1>Food I like:</h1>
      <Food />
    </main>
  </body>
</html>

If we check this out in the browser, we’ll see something weird is going on. The button isn’t working.


Interactivity using partial hydration

To make use of interactive components in Astro, we have to manually tell the Astro compiler to ship JavaScript (or interactivity) to this component.

In other words, interactivity is opt-in instead of opt-out.

This way, all components that won’t ever use interactivity get compiled down to just HTML and CSS. This keeps the Astro shipped bundle super small.

Options for Hydration

To use interactivity, there’s a client directive in Astro. These are the options for hydration:

  • client:load - instantly ships JavaScript to the component
  • client:idle - will hydrate as soon as the browser main thread is free
  • client:visible - will hydrate the component when it’s visible in the viewport
  • client:media={QUERY} - hydrates the component when the given media query is met
  • client:only={FRAMEWORK} - completely ignores the Astro compiler (not recommended)

So let’s add a client directive to the <Navigation /> component. We’ll leave the <Food /> component as-is, since it’s not interactive.

We’ll change the <Navigation /> to <Navigation client:load />:

src/pages/index.astro

<html>
  <head>
    <!-- Astro uses a JSX-like syntax in it's body -->
    <title>{title}</title>
  </head>
  <body>
    <header>
      <Navigation client:load />
    </header>
    <main>
      <h1>Food I like:</h1>
      <Food />
    </main>
  </body>
</html>

Now the menu button should work as intended on our index page!


Using Vue packages in Astro

If you decide to use Vue in its most vanilla form, just like previous examples, that’s perfectly fine. But you should know that most Vue packages are also supported in Astro as well.

So let’s take a look at using VueUse, a very popular Vue package, in Astro.

First of all, you’ll have to install the package as always:

npm i @vueuse/core

Now, write a component that uses VueUse. We use the useDraggable example from the VueUse website below, but feel free to experiment by yourself:

draggable.vue

<script setup lang="ts">
import { ref } from 'vue'
import { useDraggable } from '@vueuse/core'

const el = ref<HTMLElement | null>(null)

// `style` will be a helper computed for `left: ?px; top: ?px;`
const { x, y, style } = useDraggable(el, {
  initialValue: { x: 40, y: 40 },
})
</script>

<template>
  <div ref="el" :style="style" style="position: fixed">
    Drag me! I am at {{x}}, {{y}}
  </div>
</template>

If you now put that example inside the index.astro, it will work as intended! (don’t forget to add a client directive if needed):

index.astro

---
// This is Astro's frontmatter
// here you can write regular TypeScript or JavaScript
// It will only run on the server and never executes client-side
import Food from "../components/food.vue";
import Navigation from "../components/button.vue";
import Draggable from "../components/draggable.vue";

const title = "Hello World!";
---
<html>
  <head>
    <!-- Astro uses a JSX-like syntax in it's body -->
    <title>{title}</title>
  </head>
  <body>
    <header>
      <Navigation />
    </header>
    <main>
      <Draggable client:idle/>
      <h1>Food I like:</h1>
      <Food />
    </main>
  </body>
</html>

Deploying your Astro site

Before you start deploying your Astro site, it’s advised to give it a local preview.

Before you can generate the final result, you have to decide on a rendering strategy: either Static or SS. You can check all of the official deployment adapters here.

To locally build your site, use:

npm run build

Now you should see the final build is generated in ./dist.

In the rare case, you haven’t used a client directive anywhere, you should see there’s only HTML and CSS.

To then locally preview that ./dist, use

npm run preview

Wrapping up

As a Vue developer, Astro provides a fast and modern way to build and optimize your content-driven site using Vue components. Getting started with Astro should feel familiar to HTML but with superpowers, and will output an easy maintainable and performant result.

Download the cheatsheets

Save time and energy with our cheat sheets.