Islands Architecture
Learning Objectives
- You understand the concept of islands architecture.
- You know about micro frontends and know of the connection between islands architecture and micro frontends.
- You know of the trend of disappearing frameworks and how islands architecture fits into this trend.
When discussing Astro and Svelte, we briefly mentioned that Astro uses Islands architecture for client-side rendering of UI components.
Islands architecture
The core idea in islands architecture is that HTML content can be rendered on the server, and the HTML can contain “islands” — regions with dynamic content — that have initial server-rendered static content that can later be hydrated on the client.
As an example, an application could consist of multiple static parts such as header, main, and footer, where the main could have both static and dynamic content. In such a case, all parts would be rendered on the server. However, for the dynamic content, only the initial state would be rendered on the server, while the client would then hydrate the component, taking over the rendering.
The following figure, Fig 1., illustrates the composition of an application with both static and dynamic content:
In islands architecture, depending on the application, the content can be rendered on the server with either server-side rendering or using static site generation; frameworks like Astro allow deciding on rendering also on a page-basis. Some of the dynamic content can also be rendered on the server using hybrid rendering, where the server renders the initial state of the client-side rendered content, and the client then hydrates the component and takes over the additional rendering.
Islands architecture and micro frontends
Islands architecture can be also viewed as a continuation of micro frontends, where the frontend is divided into smaller independently developed services that are then composed into a single application. With micro frontends the development of these services can be fully separate, handled by separate teams, with different technologies, and even different deployment cycles.
In islands architecture, however, the islands are typically developed as a part of the same application.
Adding an island to MDX
Let’s consider islands concretely for the blog example that we created earlier. The blog had two pages, both created with MDX. In addition, in the chapter on Astro and Svelte from the last part, we created a Svelte component called FirstSvelteComponent.svelte
with the following content.
<script>
let count = $state(0);
</script>
<button onclick={() => count++}>Count {count}!</button>
Let’s add the component as an island to the blog.
The file is located in src/components
of the client-side project. To include the component in a blog, we can import the component in the MDX file and use it like we would use any Svelte component. Change the hello-world.mdx
file in src/content/blog
to match the following.
---
title: Hello, World!
---
import FirstSvelteComponent from "../../components/FirstSvelteComponent.svelte";
# Hello, World!
Hello, my beautiful blog!
<FirstSvelteComponent />
Above, we import the Svelte component and include it in the blog post. Note that when importing UI components in MDX files, the path is relative to the MDX file, and the import is placed outside of the front matter.
Now, when we load the blog at http://localhost:8000/blog/hello-world
, we see the button. The button is not yet interactive though. Looking at the HTML produced by the server, the JavaScript is missing.
curl localhost:8000/blog/hello-world
...
<h1 id="hello-world">Hello, World!</h1>
<p>Hello, my beautiful blog!</p>
<!--[--><button>Count 0!</button><!--]-->
That is, the application is working as expected, given how Astro handles UI component rendering.
There are also Astro integrations like astro-auto-import for automatically importing components to MDX pages. This way, there is no need to explicitly use the import
statement in MDX, which can be useful when working with large amounts of components. In the automated assessment system in this course, the astro-auto-import
is not used.
Making the component interactive
As we might recall from the chapter on Astro and Svelte, we need to use directives to tell Astro how to render the component.
Adding the client:visible
directive to the component in the MDX file tells Astro to render the component on the client side when the component becomes visible. Change the hello-world.mdx
file to match the following.
---
title: Hello, World!
---
import FirstSvelteComponent from "../../components/FirstSvelteComponent.svelte";
# Hello, World!
Hello, my beautiful blog!
<FirstSvelteComponent client:visible />
Now, when we again load the page, the component is interactive. When studying the HTML, we see that the HTML comes with a pre-rendered button with the count set to 0. In addition, the HTML includes JavaScript for loading the functionality from the server when the component enters the viewport; loading and processing the functionality leads to also hydrating the component.
curl localhost:8000/blog/hello-world
// ...
<h1 id="hello-world">Hello, World!</h1>
<p>Hello, my beautiful blog!</p>
// ... code for loading and hydration of
// ... component when it enters the viewport
<astro-island ...>
<!--[--><button>Count 0!</button><!--]--><!--astro:end-->
</astro-island>
Islands architecture and disappearing frameworks
Islands architecture can be seen as a part of a bigger trend of disappearing frameworks, where frameworks aim to minimize the amount of JavaScript loaded by the client, even to the extent where no JavaScript is loaded at all. The approach works well with multi-page applications where some pages can be fully static, while others may some dynamic content.
Disappearing frameworks are a response to the increasing complexity of web applications, where the amount of JavaScript loaded by the client has grown significantly. The complexity has led to slower loading times, increased memory usage, and decreased battery life on mobile devices.
Islands and application-specificity
It is worthwhile to note that the interactivity provided by the islands do not have to be tied to the APIs provided by a specific application. Individual islands can rely on third-party APIs for their interactivity — that is, islands architecture can also be used to create interactive components that are not tied to the application’s backend.
In practice, islands could also be imported to an application as a dependency, rendering the placeholder of the island from the dependency on the server. Upon hydration, the code from the dependency would be used, where the code could as well fully function with a third-party service. This would be closer to the micro frontend approach, where micro frontends are fully separate applications that are then composed into a single application.
Creating and loading the islands as fully separate components could be useful for creating interactive functionality that is shared across multiple applications, or for creating interactive components that are not part of the application’s core functionality. In the case of islands architecture, however, the server renders the initial state of the island, so some part of the island needs to be available during rendering on the server.