You Are Using Astro Wrong: A Survival Guide
Some rules to keep your team on the happy path. Break them with caution :)
I am one of Astro’s maintainers. Biased. Opinions are my own.
Astro is a framework for fast websites. When everything works, you can be highly productive in building interactive content websites. However, you often have to discover what doesn’t work the hard way.
This is my opinionated retrospective from the past few months of support threads. I share caveats that are not well-known, features that hurt more than they help, and mindsets that invite work than value.
This is all aimed at teams working on a large project. If you are building a personal website or simply exploring, don’t listen to me - and by all means, spread your wings. A lesson learned is better than a lesson taught ;)
Do not trust the dev server
The dev server is fast but it is also a completely different architecture from the real app. You can think of it as a mimic of the production result. Likewise, the sets of bugs in the dev server and in the production build are completely independent with little overlap.
You can expect the dev server to represent visual changes accurately, but it will miss nuances of the build process. You have to run the full build frequently during the development process to make sure you are on the right track.
For SSR, if you deploy to a serverless platform that does not make it easy to run the final build locally, you might be able to use the node adapter during development.
Do not “migrate”
Astro is not an alternative to React, Vue or Svelte. Instead, it completes them. It renders components written in these frameworks to HTML at build-time, and lets you pick and choose which ones should also include code to maintain interactivity in the browser.
The inclusion of a templating language might be misleading, but it does not compete with frameworks. Rather, it acts only as the glue between them.
It is perfectly supported to write most of your app in JSX, for example, and build upon those parts exclusively. Your SPA app will live happily embedded within an Astro project:
---
// src/pages/[...allroutes].astro
import ReactApp from "../react/app.tsx"
---
<ReactApp location={Astro.request.url} client:load/>
In the code example above, […allroutes].astro
is the only page within the project, and handles every request to the server. The client:load
directive will make the component render on the server, hydrated when the page loads on the client. ReactApp is built using React Router, which supports receiving location as a prop and will render the relevant react components on the server using it.
Update (January 2024): I have published a starter template built on this idea. See it live. See it on GitHub.
Do not use define:vars
If the other points are footguns, this is a landmine.
You can add this directive to a script tag as a quick and easy way to use data loaded by the server. It has the well-documented downside that you can’t use npm libraries. However, more annoyingly, it implicitly turns your module script into a classic script.
Sooner or later, you and your teammates will find yourself reading articles written by V8 engineers about the history of JavaScript and the implications of “defer”, “async”, “type=module”, and how every combination of the three results in slightly different timing between your code, your libraries’ code, and DOM loading. No one using define:vars today has used it without stepping through these thorns first.
If you find yourself considering using the DOMContentLoaded event, go back to your problem, and find another way.
Do not use View Transitions
Multi-page view transitions is an incredible upcoming web feature. They are implemented in Astro today using the single-page API instead, in what’s called the “SPA mode”.
Adopting view transitions will seem delightfully easy at first, but it has subtle and potentially time-consuming implications. Every time you navigate, Astro’s code (instead of the browser’s) fetches the new page and replaces the contents of the current page with it, using the startViewTransition feature built-in to Chrome for the animations. Since the navigation was not done by the browser itself, you are on the same page as far as it is concerned, and it does not rerun scripts that are common to the two pages.
To remedy this, you may be to use one of the five lifecycle events and rerun the important parts you control. However, if a library is misbehaving instead, there is nothing you can do other than forking it and making the changes there.
Vet your dependencies
Named export 'Types' not found. The requested module 'mongoose' is a CommonJS module, which may not support all module.exports as named exports.
If you have encountered an error like this one or one that made just as little sense, what you probably encountered was a mispackaged NPM library.
Astro attempts to process all libraries to turn them into module scripts. However, the JavaScript ecosystem is in a state where this process is unnecessarily complicated - mostly because some libraries simply lie about how they are organized. Luckily, there is a simple website that can tell you if your dependencies will behave well: publint.dev.
Do not get fancy with Tailwind
Tailwindcss as a tool is volatile. It has been the subject of several unresolved support threads, often due to bugs that are not reproducible. Unsurprisingly, it is mispackaged - half written in CommonJS and half in ES Modules.
Do not try to configure tailwind unless it is absolutely necessary. Embrace its simplicity and copy-paste the utility classes.
Do not use more than 5 integrations
Astro’s integration API is very powerful… a little too powerful for its own good. It seems to have been created with first-party integrations in mind and exposes several internals - internals that move around frequently with Astro’s pace of innovation. The more of them you have in your project, the more likely it is that one of them breaks with a version update.
Final Word
Avocado.
Hi, nice artcile.
But how to implement this
<ReactApp location={Astro.request.url} client:load/> ??
Im trying here, but i received the logical error document is not defined?
You can show me how?