npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

vitest-browser-astro

v0.1.0

Published

Vitest browser mode renderer for Astro

Readme

vitest-browser-astro

Test Astro components in real browsers with Vitest Browser Mode.

Astro components render server-side using the Container API. This library enables browser testing by rendering components on the server and injecting the HTML into the browser DOM. Tests run with real browser APIs.

Installation

npm install -D vitest-browser-astro @vitest/browser playwright

Using pnpm:

pnpm add -D vitest-browser-astro @vitest/browser playwright

Note: This package currently requires Vitest 3.x. Vitest 4 is not yet compatible with Astro.

Quick Start

Create vitest.config.ts:

import { getViteConfig } from "astro/config";
import { astroRenderer } from "vitest-browser-astro/plugin";

export default getViteConfig({
	plugins: [astroRenderer()],
	test: {
		browser: {
			enabled: true,
			name: "chromium",
			provider: "playwright", // or 'webdriverio'
			headless: true,
		},
	},
});

For Astro pages that contain framework components (React, Vue, etc.), add renderers using getContainerRenderer() from your framework integration packages - see Plugin options.

Write a test file:

// src/components/Card.test.ts
import { render } from "vitest-browser-astro";
import { expect, test } from "vitest";
import Card from "./Card.astro";

test("renders card title", async () => {
	const screen = await render(Card, {
		props: {
			title: "Hello World",
		},
	});

	await expect.element(screen.getByText("Hello World")).toBeVisible();
});

Run tests:

npx vitest

See the test fixture for a complete working example.

Configuration

Plugin options

The astroRenderer plugin uses the Astro Container API (experimental) for framework support. Configure it with getContainerRenderer() from your framework integration packages:

import { getContainerRenderer as getReactRenderer } from "@astrojs/react";
import { getContainerRenderer as getVueRenderer } from "@astrojs/vue";

astroRenderer({
	renderers: [getReactRenderer(), getVueRenderer()],
});

Options:

  • renderers - Array of framework renderers obtained from getContainerRenderer(). Pass these if your Astro components use framework integrations.

Note: Only one JSX-based framework (React, Preact, or Solid) can be used at a time. Non-JSX frameworks (Vue, Svelte) can be combined with any JSX framework.

See the Container API renderers documentation for more details.

Testing Astro Components

props and slots

Render components with props and slots by passing them to render():

import { render } from "vitest-browser-astro";
import { expect, test } from "vitest";
import Card from "./Card.astro";

test("renders slot content", async () => {
	const screen = await render(Card, {
		props: { title: "Card Title" },
		slots: {
			default: "<p>Main content</p>",
			footer: "<small>Footer text</small>",
		},
	});

	await expect.element(screen.getByText("Main content")).toBeInTheDocument();
	await expect.element(screen.getByText("Footer text")).toBeInTheDocument();
});

Props are serialized using devalue, and can be JSON primitives, Dates, RegExps, Maps, or Sets.

Interactive components with scripts

Components with inline scripts work without additional configuration:

---
// Toggle.astro
interface Props {
	label: string;
}
const { label } = Astro.props;
---

<button data-testid="toggle">
	{label}: <span data-testid="state">OFF</span>
</button>

<script>
	document.querySelectorAll('[data-testid="toggle"]').forEach((button) => {
		const stateEl = button.querySelector('[data-testid="state"]');
		let isOn = false;

		button.addEventListener("click", () => {
			isOn = !isOn;
			stateEl.textContent = isOn ? "ON" : "OFF";
		});
	});
</script>

Example test:

import { render } from "vitest-browser-astro";
import { userEvent } from "@vitest/browser/context";
import { expect, test } from "vitest";
import Toggle from "./Toggle.astro";

test("toggles state on click", async () => {
	const screen = await render(Toggle, {
		props: { label: "Power" },
	});

	const state = screen.getByTestId("state");
	await expect.element(state).toHaveTextContent("OFF");

	await userEvent.click(screen.getByTestId("toggle"));
	await expect.element(state).toHaveTextContent("ON");

	await userEvent.click(screen.getByTestId("toggle"));
	await expect.element(state).toHaveTextContent("OFF");
});

Testing Framework Components

Framework components (e.g. React, Vue, Svelte) require changes in the Astro and Vitest config:

Setup

Add the framework integration to astro.config.mjs:

import { defineConfig } from "astro/config";
import react from "@astrojs/react";

export default defineConfig({
	integrations: [react()],
});

Configure framework renderers in vitest.config.ts using getContainerRenderer():

import { getViteConfig } from "astro/config";
import { astroRenderer } from "vitest-browser-astro/plugin";
import { getContainerRenderer as getReactRenderer } from "@astrojs/react";

export default getViteConfig({
	plugins: [
		astroRenderer({
			renderers: [getReactRenderer()],
		}),
	],
	test: {
		browser: {
			enabled: true,
			name: "chromium",
			provider: "playwright",
			headless: true,
		},
	},
});

Testing hydrated components

Framework components with client directives require hydration before becoming interactive. Use client:load directive and the waitForHydration() utility:

import { render, waitForHydration } from "vitest-browser-astro";
import { userEvent } from "@vitest/browser/context";
import { expect, test } from "vitest";
import Counter from "./Counter.astro";

test("React counter increments on click", async () => {
	const screen = await render(Counter);

	// Wait for hydration to complete
	await waitForHydration(screen);

	const count = screen.getByTestId("count");
	const button = screen.getByTestId("increment");

	await expect.element(count).toHaveTextContent("0");

	await userEvent.click(button);
	await expect.element(count).toHaveTextContent("1");
});

Example component:

---
// Counter.astro
import ReactCounter from "./ReactCounter.tsx";
---
<ReactCounter client:load />

Astro wraps framework components in <astro-island> elements with an ssr attribute during server-side rendering. This attribute is removed once client-side hydration completes.

The waitForHydration() function waits for this attribute to be removed before proceeding.

Pass the screen result to wait for all islands, or a specific locator to wait for islands within that element:

// Wait for all islands in the component
await waitForHydration(screen);

// Wait for islands within a specific element
await waitForHydration(screen.getByTestId("header"));

Skip waitForHydration() for components without client directives.

Supported frameworks

Any front-end framework with an Astro adapter is supported. Only one JSX-based framework (React, Preact, or Solid) can be used at a time because the Astro Container API cannot distinguish between different JSX frameworks in the same test configuration. Non-JSX frameworks (Vue, Svelte) can be combined with any JSX framework.

User Interactions

Use userEvent from @vitest/browser/context for simulating user interactions:

import { userEvent } from "@vitest/browser/context";

await userEvent.click(button); // Click elements
await userEvent.type(input, "Hello"); // Type text
await userEvent.keyboard("{Enter}"); // Keyboard events
await userEvent.hover(element); // Hover

API Reference

render(component, options?)

Renders an Astro component and returns query utilities.

const screen = await render(Component, {
	props: { title: "Hello" },
	slots: { default: "<p>Content</p>" },
});

Returns an object which includes Vitest Browser's locator selectors and an .element() method.

  • element() - Returns the DOM element containing the rendered component
  • getByRole(role) - Find element by ARIA role
  • getByAltText(altText) - Find element by alt text
  • getByLabelText(labelText) - Find element by associated label text
  • getByPlaceholder(placeholderText) - Find element by placeholder text
  • getByText(text) - Find element by text content
  • getByTitle(title) - Find element by title attribute
  • getByTestId(id) - Find element by data-testid attribute

waitForHydration(locator)

Waits for framework component hydration to complete.

// Wait for all islands
await waitForHydration(screen);

// Wait for islands within a specific element
await waitForHydration(screen.getByTestId("footer"));

cleanup()

Removes all rendered components from the DOM. Called automatically between tests.

Troubleshooting

Framework components not hydrating

  1. Add the framework renderer using getContainerRenderer() in plugin options
  2. Add the corresponding integration to astro.config.mjs
  3. Use client:load directive on framework components
  4. Call waitForHydration() before interacting with the component

For more issues, see GitHub Issues.

Requirements

  • Astro 5.x or later
  • Vitest 3.x (Vitest 4 is not yet compatible with Astro)
  • Vite 6.x or later

Contributing

Contributions are welcome. Check the GitHub repository for issues and pull requests.

License

MIT © Matt Kane