Astro loading spinners

Last updated 8 December 2023

Astro

2 min read

If you use a framework component with a client directive of client:only, you may see a flash of no content until the component renders.

In this case, it may be useful to show a loading state like a spinner or skeleton until the component loads - especially if it will take longer than a second.

One way to do this is to use a separate loading element alongside your framework component, and hide or remove that element once the component mounts.

React example

*.astro
{/* some sort of indicator or spinner */}
<svg
class="spinner"
...
>
</svg>
<ReactComponent client:only="react" />
ReactComponent.tsx
import { useEffect } from "react";
export function ReactComponent() {
useEffect(() => {
const spinner = document.querySelector(".spinner");
if (spinner) spinner.remove();
}, []);
return <div>React content</div>;
}

Vue example

*.astro
<VueComponent client:only="vue" />
VueComponent.vue
<script setup lang="ts">
import { onMounted } from "vue";
onMounted(() => {
const spinner = document.querySelector(".spinner");
if (spinner) spinner.remove();
});
</script>
<template>
<div>Vue content</div>
</template>

Svelte example

*.astro
<SvelteComponent client:only="svelte" />
SvelteComponent.svelte
<script lang="ts">
import { onMount } from 'svelte';
onMount(() => {
const spinner = document.querySelector(".spinner");
if (spinner) spinner.remove();
});
</script>
<div>Svelte content</div>

Alternative

If you’re using another directive like client:load or client:visible, your component’s initial view (before it hydrates) may render right away.

In this case, you probably don’t need to do anything. But if you still need to show a loading state, then you can handle this within the component itself. For example, you could add a loading spinner within the component, and then conditionally render the final view once it’s ready (with state).