Home Web Front-end JS Tutorial First impressions of Astro: what I liked and disliked

First impressions of Astro: what I liked and disliked

Dec 29, 2024 am 10:34 AM

First impressions of Astro: what I liked and disliked

I recently started using Astro to rebuild side projects that were originally built with WordPress, Go, Rails, and Hugo. I picked Astro because it had a React-like DX with good language server support, and because it was compatible with serverless hosting platforms that had a generous free tier (Cloudflare, AWS Lambda, etc).

I didn't know much about Astro when I started using it. Now that I've migrated multiple sites, I wanted to share what I liked and disliked about the framework for anyone else considering using it.

Astro: what I liked

At its core, Astro is a static site generator with the ability to produce dynamic server-rendered pages when needed. Astro is a great fit for blogs or small marketing sites with limited interactivity. The framework provides most of the DX from Next.js without the React.js overhead.

Good IntelliSense and code formatting in a server rendered template language

Let's be honest: good language server support and code formatting are severely lacking in traditional server-rendered templating languages. Go templates, Jinja, ERB, and EJS are painfully behind with tooling when compared to their React/Vue/Svelte counterparts. Most server-rendered templating languages have no way of knowing what variables are in scope or what their types are.

With Astro, all of these features are one VS Code extension away.

Inside of Astro templates, you set your data at the top of the template inside of a "code fence" that either runs at build time or when responding to a request on the server. Here's what that looks like in practice:

---
import Layout from "../layouts/Layout.astro";
import { getPosts } from "../data/posts";

const posts: { id, title }[] = await getPosts();
---
<Layout pageTitle="Posts">
  <h1>Posts</h1>

  {post.length > 0 ? (
    <ul>
      {posts.map((post) => (
        <li>
          <a href={`/posts/${post.id}`}>
            {post.title}
          </a>
        </li>
      )}
    </ul>
  ) : null}
</Layout>
Copy after login
Copy after login

Because all of the data for the template is loaded in the "code fence" above the template, the language server can provide auto-completion for the properties of any object defined within the scope. It will also indicate when you are trying to use a variable that doesn't exist.

Astro files can be "components"

One of my biggest gripes with traditional templating languages like Go templates, Jinja, and EJS is that they don't have "components" that can accept children. Most of my websites have a constrained-width "container" UI element of some kind, which ensures that content doesn't fly out to the end of the screen on ultra-wide monitors. If you have a .container class that you manually add to

elements, then this usually works fine. However, if you're using a utility CSS framework like Tailwind then you may find yourself adding the following code to every single page template:

<div
 >



<p>When you eventually need to change these classes, it's an error-prone pain to update each file manually. But if your templating language doesn't have components that can accept children, it's almost inevitable. </p>

<p>Unlike those traditional templating languages, Astro templates can be used as components that accept children using a <slot /> tag. A long string of utility classes could be extracted into a reusable Astro component:<br>


<pre class="brush:php;toolbar:false"><div
 >



<p>The Astro component could then be consumed from another Astro file.<br>
</p>

<pre class="brush:php;toolbar:false">---
import Container from "../components/Container.astro";
---
<Container>
  <h1>Hello, world!</h1>
</Container>
Copy after login
Copy after login

Astro files aren't limited to a single slot: they can have multiple.

My favorite feature of Astro components is that they can accept props within the code fence. Here's an example:

---
import Layout from "../layouts/Layout.astro";
import { getPosts } from "../data/posts";

const posts: { id, title }[] = await getPosts();
---
<Layout pageTitle="Posts">
  <h1>Posts</h1>

  {post.length > 0 ? (
    <ul>
      {posts.map((post) => (
        <li>
          <a href={`/posts/${post.id}`}>
            {post.title}
          </a>
        </li>
      )}
    </ul>
  ) : null}
</Layout>
Copy after login
Copy after login

The component can then accept props when used within another file.

<div
 >



<p>When you eventually need to change these classes, it's an error-prone pain to update each file manually. But if your templating language doesn't have components that can accept children, it's almost inevitable. </p>

<p>Unlike those traditional templating languages, Astro templates can be used as components that accept children using a <slot /> tag. A long string of utility classes could be extracted into a reusable Astro component:<br>


<pre class="brush:php;toolbar:false"><div
 >



<p>The Astro component could then be consumed from another Astro file.<br>
</p>

<pre class="brush:php;toolbar:false">---
import Container from "../components/Container.astro";
---
<Container>
  <h1>Hello, world!</h1>
</Container>
Copy after login
Copy after login

A frontend asset pipeline with live reloads is built-in

I've built my own server-side integrations with Vite before. If you're trying to get something online quickly, this is the kind of commodity feature that you want to avoid building yourself. With Astro, it's built-in.

If you want to add a custom script to an Astro page or component, all you have to do is drop a script tag on the page.

---
type Props = { pageTitle: string; pageDescription: string };

const { pageTitle, pageDescription } = Astro.props;
---

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>{pageTitle}</title>
    <meta name="description" content={pageDescription} />
  </head>
  <body>
    <slot />
  </body>
</html>
Copy after login

Astro will automatically compile TS and JS files as a part of a site's build process. You can also write scripts that use imports from node_modules inside of a script tag within an Astro component, and Astro will compile that during build time and extract it to its own file.

---
import Layout from "../layouts/Layout.astro";
---
<Layout
  pageTitle="Tyler's Blog"
  pageDescription="I don't really post on here."
>
  <h1>Tyler's blog</h1>
  <p>Sorry, there's no content yet...</p>
</Layout>
Copy after login

You can include CSS or Scss styles in an Astro file by importing them within the code fence.

<div>
  <h1>This is my page</h1>
  <script src="../assets/script.ts"></script>
</div>
Copy after login

Astro also provides the ability to do scoped styles by using a style tag in an Astro file. This feature may be familiar to Vue users.

Given the following Astro file:

<script>
  // This will also compile down to a JavaScript file at build time.

  import axios, { type AxiosRequestConfig, type AxiosResponse } from "axios";

  const response = await axios
    .get<AxiosRequestConfig, AxiosResponse<{foo: string}>>("/some-endpoint");

  console.log(response.data.foo);
</script>
Copy after login

Easy, right?

Actions

Actions provide a type-safe way of running backend functions. They provide validation and can handle both JSON and form data. They're easily one of Astro's killer features: all of this would need to be hand-wired in a Next.js app in a bespoke way. They require a little more code than can fit neatly into an example, but they're pretty elegant to use. I'd recommend reading the actions docs page.

No runtime JS cost

There are an endless number of Twitter devs that say React is "fast enough." For a lot of things it's not.

I use Rasbperry Pi 4s for little projects, and you can feel the runtime cost of React. I'm sure it's the same for inexpensive Android phones, except in that case the JS overhead will also drain the battery.

If the only interactivity that my site needs is toggling a nav menu, I'd rather wire that up myself. I'll happily reach for React when I need it, but for so many projects I don't actually need it.

Astro: what I disliked

The things that I dislike about Astro are not unique to the framework: they are ideas borrowed from other tools in the JavaScript ecosystem.

File-based routing

Because Astro employs file-based routing, half of the files in an Astro project end up named index.(astro|js|ts) or [id].(astro|js|ts). File-based routing is an obnoxious pattern that swept the frontend world by storm after Next.js implemented it, and it comes with many drawbacks:

  • You'll often have 5 tabs open in your editor with the same filename. It will take a minimum of 3 guesses to find the tab you're looking for.
  • The editor's file fuzzy finder is far less useful because so many files have the same name.
  • File-based routing scatters one of an application's core concerns across many files and folders, making it difficult to see all routes at a glance within the editor. Instead, you must drill down into several folders to understand what pages are available.

I'll admit: file-based routing feels great when you're making a site with under 10 pages. But as a site grows it adds friction, and you fight the feature more than you benefit from it.

In the JavaScript ecosystem, Remix stands apart by offering a version of file-based routing that flattens all routes into a single directory, and allows a user to opt out of file-based routing entirely with manual route configuration.

File-based routing is my biggest complaint about Astro, but it's a difficult feature to escape. It is implemented in Next.js, Nuxt.js, SvelteKit, and others. What's even stranger is that these frameworks are largely unopinionated about the filenames for other parts of the application. In start contrast to Ruby on Rails, most JavaScript frameworks give you a great degree of freedom in file names and project structure–except for routing. It's a special case, and special cases add complexity to software.

One component per file (kind of)

A JavaScript language feature that I really like is the ability to define multiple variables, functions, and classes in a single file. This makes it easy to colocate related functionality without having to prematurely extract it to other files because of language-level constraints.

Much like Vue's single-file components, Astro files allow defining one component per file. This feels tedious to me, but Astro provides a workaround.

Astro can embed pre-rendered React, Vue, Svelte, Solid, and Preact components directly into its templates without loading any client-side JavaScript. Preact components pair reasonably well with Astro because Preact JSX is much closer to HTML than React JSX. It does become awkward managing both Astro and Preact components in the same project though, and once I begin using Preact components I find myself moving most of the UI out of Astro files and into Preact.

Final thoughts on Astro

If you're an avid user of Next.js, Nuxt, or SvelteKit and you are happy with your framework, you might not get much out of Astro. However, if you want to ship less JavaScript bloat to your users while retaining the DX of something like Next.js, then Astro might be for you.

Astro is geared towards content-driven websites, and provides markdown support out-of-the-box. Because of its focus on content, it is an ideal developer blogging platform to replace a WordPress or Hugo site. However, it's capable of much more than just content sites through features like Actions.

Despite my strong distaste for file-based routing, my biggest concern with adopting Astro is the potential for breaking changes that would force me to rebuild my sites. JavaScript tools are much more aggressive with breaking changes than tools you find in other language ecosystems. Because I'm so new to Astro, I don't know how much changes from one major version to the next. Even with this concern, I plan to move 5-to-6 of my sites from other platforms to Astro so I can take advantage of its top-notch DX and host the sites inexpensively.

The above is the detailed content of First impressions of Astro: what I liked and disliked. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

Hot Topics

Java Tutorial
1664
14
PHP Tutorial
1268
29
C# Tutorial
1246
24
The Evolution of JavaScript: Current Trends and Future Prospects The Evolution of JavaScript: Current Trends and Future Prospects Apr 10, 2025 am 09:33 AM

The latest trends in JavaScript include the rise of TypeScript, the popularity of modern frameworks and libraries, and the application of WebAssembly. Future prospects cover more powerful type systems, the development of server-side JavaScript, the expansion of artificial intelligence and machine learning, and the potential of IoT and edge computing.

JavaScript Engines: Comparing Implementations JavaScript Engines: Comparing Implementations Apr 13, 2025 am 12:05 AM

Different JavaScript engines have different effects when parsing and executing JavaScript code, because the implementation principles and optimization strategies of each engine differ. 1. Lexical analysis: convert source code into lexical unit. 2. Grammar analysis: Generate an abstract syntax tree. 3. Optimization and compilation: Generate machine code through the JIT compiler. 4. Execute: Run the machine code. V8 engine optimizes through instant compilation and hidden class, SpiderMonkey uses a type inference system, resulting in different performance performance on the same code.

Python vs. JavaScript: The Learning Curve and Ease of Use Python vs. JavaScript: The Learning Curve and Ease of Use Apr 16, 2025 am 12:12 AM

Python is more suitable for beginners, with a smooth learning curve and concise syntax; JavaScript is suitable for front-end development, with a steep learning curve and flexible syntax. 1. Python syntax is intuitive and suitable for data science and back-end development. 2. JavaScript is flexible and widely used in front-end and server-side programming.

JavaScript: Exploring the Versatility of a Web Language JavaScript: Exploring the Versatility of a Web Language Apr 11, 2025 am 12:01 AM

JavaScript is the core language of modern web development and is widely used for its diversity and flexibility. 1) Front-end development: build dynamic web pages and single-page applications through DOM operations and modern frameworks (such as React, Vue.js, Angular). 2) Server-side development: Node.js uses a non-blocking I/O model to handle high concurrency and real-time applications. 3) Mobile and desktop application development: cross-platform development is realized through ReactNative and Electron to improve development efficiency.

How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration) How to Build a Multi-Tenant SaaS Application with Next.js (Frontend Integration) Apr 11, 2025 am 08:22 AM

This article demonstrates frontend integration with a backend secured by Permit, building a functional EdTech SaaS application using Next.js. The frontend fetches user permissions to control UI visibility and ensures API requests adhere to role-base

Building a Multi-Tenant SaaS Application with Next.js (Backend Integration) Building a Multi-Tenant SaaS Application with Next.js (Backend Integration) Apr 11, 2025 am 08:23 AM

I built a functional multi-tenant SaaS application (an EdTech app) with your everyday tech tool and you can do the same. First, what’s a multi-tenant SaaS application? Multi-tenant SaaS applications let you serve multiple customers from a sing

From C/C   to JavaScript: How It All Works From C/C to JavaScript: How It All Works Apr 14, 2025 am 12:05 AM

The shift from C/C to JavaScript requires adapting to dynamic typing, garbage collection and asynchronous programming. 1) C/C is a statically typed language that requires manual memory management, while JavaScript is dynamically typed and garbage collection is automatically processed. 2) C/C needs to be compiled into machine code, while JavaScript is an interpreted language. 3) JavaScript introduces concepts such as closures, prototype chains and Promise, which enhances flexibility and asynchronous programming capabilities.

JavaScript and the Web: Core Functionality and Use Cases JavaScript and the Web: Core Functionality and Use Cases Apr 18, 2025 am 12:19 AM

The main uses of JavaScript in web development include client interaction, form verification and asynchronous communication. 1) Dynamic content update and user interaction through DOM operations; 2) Client verification is carried out before the user submits data to improve the user experience; 3) Refreshless communication with the server is achieved through AJAX technology.

See all articles