1. 16
    Add Interactive React Components to a Static Astro Page with Astro Islands
    7m 56s

Add Interactive React Components to a Static Astro Page with Astro Islands

Lazar Nikolov
InstructorLazar Nikolov

Share this video with your friends

Send Tweet
Published 5 months ago
Updated 3 months ago

Everything up to this point Astro builds statically which means there is no JavaScript loaded on any of our pages. It's all loaded during build time and served as static files on the web

When we do want to add some interactivity into our site we can use Astro Islands to leverage JavaScript on our pages.

Astro islands are interactive components that Astro treats differently than the rest. Instead of just executing them during build time and saving the output, Astro will also hydrate the components at a specific point and ship the JavaScript needed for the component to be interactive.

To use a UI framework, we need to add its integration, so we did npx astro add react to add the react integration.

Then we will create an ordinary react component, just as we would in a react page, and import it and place it in a page. To mark that component as an island, we use a client directive. The client load directive hydrates the component during the initial page load.

The client directive has several different loading strategies you can use based on your usecase:

  • client:load
  • client:idle
  • client:visible
  • client:media
  • client:only

Lazar Nikolov: [0:00] So far, we've learned that by default, Astro builds our website statically, unless we specify that we want to render server-side. The static rendering ships zero JS because it executes all of the JavaScript we write during the build time, and then it saves the output as static HTML and CSS files.

[0:17] What if we want to have interactive elements on our page, elements that require JavaScript to handle specific interactions, like making API calls after a user action? Thanks to Astro's island architecture, that's possible.

[0:32] Astro calls those interactive elements islands. They are ordinary components that need to execute JavaScript at runtime in order to work. We can also have multiple islands on our page. Since Astro allows us to load JavaScript code in our components, using UI frameworks like React, Vue, Svelte, and Solid is supported out of the box.

[0:53] We could still use those frameworks even without the islands, but as I mentioned, Astro would just render their output as static HTML so no interactivity would be available, even though we're talking about React or Vue components.

[1:06] To understand the Astro islands better, let's build a Like button component for our articles page. First, we need to add the UI framework integration. Astro comes with a built-in integration for React, which we can add by running npx astro add react. This will install all the necessary packages we need to run React and configure Astro to include React's runtime in our React components.

[1:31] Let's just proceed. If we check our Astro config file, we're going to see that there is the React integration added to our configuration object. Now let's create a new component in src/components and name it PostLike.tsx. This is a plain old React component.

[1:50] Let's export a default function called postLike that returns a button element. To make it look nice, I'm just going to add the following classes. To add the postLike component into our article page, all we need to do is just import it as a regular component, just like we're importing the layout component. Now we can place it below the content.

[2:16] If we leave it like this, Astro will run the React component during build time and save its output as HTML. This is not an island, at least not just yet. We need to tell Astro that this is an island component and that it needs to hydrate it.

[2:31] Hydrating components, or just hydration, is the process of taking pre-rendered static content and making it interactive on the client side. In our case, this involves loading React's runtime and using the actual static DOM to create its virtual DOM so that React can continue working from that point on.

[2:51] To tell Astro to hydrate our component and turn it into an island, we need to use a client directive on it. Client directives control how islands are hydrated on the page. If we want to immediately hydrate our like button, we're going to add the client:load directive. You can read this as hydrate this component on the client immediately on page load.

[3:15] Let's run the app now. Instead of just a button, Astro added the Astro island element that has attributes that define which is the component, which is the renderer, and how to hydrate it.

[3:27] If we open the network tab and refresh, we're going to see that we're now loading a few JavaScript files like the before-hydration-JavaScript file, the Astro-react-client-JavaScript file, the react-jsx-dev-runtime, etc. This is our first island. Let's demonstrate the interactivity.

[3:47] Let's open the post-like component and add an onClick method to the button. We're going to show an alert, and it's going to say, "Hello, Island." Clicking the button now will show the Hello-Island alert as expected.

[4:01] If we were to remove the client-load-directive and try to click the button again, nothing happens. There's no JavaScript, therefore there's no interactivity. Let's bring our directive back.

[4:14] One thing to note, and you might have noticed this, is that when we are in this component, we are in React land. You might have noticed that I used class name instead of just class. You might mix them at the beginning, but that's OK, you'll get used to them. Let's see different ways to hydrate our component.

[4:32] First, let's open the content and duplicate the text inside just so we can make the article seem a bit longer to read. That's good. Let's also open the network tab and show only the JavaScript file requests. We'll hit refresh, and we'll see that we're loading the post-like immediately after page load. This is how a client-load component is behaving.

[4:59] Let's change this to client-idle and hit refresh. Seemingly it's the same, but this time Astro will load the component after the page has loaded initially, which is better for performance. We use the idle directive on low-priority UI elements that don't need to be immediately interactive. Aside from the idle, we also have the visible directive.

[5:20] Let's demonstrate that. We can see that React and our component aren't loaded just yet, but when I scroll down in the article, just as it's about to become visible -- there we go -- these JS files gets loaded immediately.

[5:35] This has the lowest priority for hydration. It's good to be used on lower components that are further down the page, components that aren't visible above the fold. Imagine that this was a resource-intensive component. If the user doesn't scroll down to it, it will never load, and that's pretty good.

[5:54] There's a couple more directives. Media will load and hydrate the component only for certain CSS media queries, for example, mobile menu buttons, etc. There's only, which skips HTML server rendering and renders the component only on the client side.

[6:11] It acts like the load directive. It loads, renders, and hydrates the component immediately on page load. When using the only directive, you have to pass the correct framework as a value. Since Astro doesn't run the component during build time, it doesn't know what framework you're using in it, so you have to explicitly define it.

[6:32] That's how we can control which component will load JavaScript and when it will load its JavaScript. Let's do a recap now. We learned that islands are interactive components that Astro treats differently than the rest.

[6:46] Instead of just executing them during build time and saving the output, Astro will also hydrate the components at a specific point and ship the JavaScript needed for the component to be interactive.

[6:58] To use a UI framework, we needed to add its integration, so we did npx astro add react to add the React integration. Then we created an ordinary React component, just as we would in a React page, and imported it and placed it in the page.

[7:14] To mark that component as an island, we use the client directive. The client:load directive hydrates the component during the initial page load. The client:idle component hydrates it after the page loads. The client:visible hydrates it only and if it gets visible. The client:media hydrates it only when the media query is met, and the client:only skips the server side pre-rendering and hydrates it only on the client side during the initial page load.

[7:45] Using these client directives in a smart way will make our website load faster and ship less files, which in return will improve the user experience of our website.