I recently converted a personal project written with Gatsby, React, and Redux to Astro and React. Two things were more complicated than I expected:

  1. Passing routing information through a query parameter in the URL
  2. Retaining global state through page navigation

Query Parameters

Astro does not currently have a built-in way to access or parse a query string from a URL. According to a Stack Overflow post, the Astro team does not consider this within the scope of the framework.

Parsing a query string for the parameter value can still be done with traditional JavaScript and browser API’s:

// get 'calico' from '?cats=calico'
const urlParams = new URLSearchParams(window.location.search);
const catsParam = urlParams.get('cats');

However, the component will need to be marked with a client:load directive.

<CatsWidget client:load />

This only became complicated in my case because I wanted to persist global state in JavaScript across page navigation.

Global State and Navigation

In a pure React or single-page application, and with certain set-ups in Gatsby, your application and its state has an immediate separation from the URL. You can start to think of and treat the URL and browser history as an auxiliary service. For the convenience of the user, you retro-fit normal browser navigation back into your application, just kind of updating the URL as you think best.

An Astro application is a big step closer to making a real website again. Loading a page is like…loading a page? as opposed to modifying a running application and maybe updating the URL, or loading a full application and then shoehorning in a certain state based on the URL and what can be retrieved from IndexedDB.

Astro even uses traditional anchor tags:

<a href={nextLocation} />

When converting this project to Astro, it threw me off that loading a new page “reset” my JavaScript and nanostore state. I was writing React components and using a global state manager. I didn’t think through the possibility that navigating to a new page would actually cause a new page with new state to render.

The quick way around my problem was to use @nanostores/persistent. It’s more or less an extension of nanostore that allows stores to be created that are saved in localStorage. Thus, I could create a store in Astro that persisted state as the user navigated between pages.

Ultimately, my application code feels a bit more complicated now that it’s running on Astro. Astro made everything else simpler (e.g. upgrades, build configurations, deployment), but it feels like there’s some new tribal knowledge to know of which components are server-side and which are client loaded and what types of state can be passed between them. I think I could have moved certain data transformations so that it was all purely server-side, except that I wanted to pass state through a query parameter, necessitating client loaded components and a hard separation. I think that in the future I’m going to move server and client components to separate directories.