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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@hvniel/vite-plugin-svelte-inline-component

v0.0.16

Published

> Write tiny Svelte components straight inside your JavaScript / TypeScript tests using tagged‑template literals.

Readme

@hvniel/vite-plugin-svelte-inline-component

Write tiny Svelte components straight inside your JavaScript / TypeScript tests using tagged‑template literals.


📖 Table of Contents


✨ What it does

The plugin lets you write Svelte components directly in your .ts or .js test files. It finds every template literal whose tag matches a list you choose (defaults to html and svelte), compiles the markup with the Svelte compiler, and replaces it with an import of a virtual module that exports the compiled component. No extra files.

🔧 Installation

pnpm add -D @hvniel/vite-plugin-svelte-inline-component
# or
npm i -D @hvniel/vite-plugin-svelte-inline-component
# or
yarn add -D @hvniel/vite-plugin-svelte-inline-component

🚀 Usage

vite.config.ts / vite.config.js

import { defineConfig } from "vite";
import { sveltekit } from "@sveltejs/kit/vite";
import inlineSveltePlugin from "@hvniel/vite-plugin-svelte-inline-component/plugin";

export default defineConfig(({ mode }) => ({
  plugins: [mode === "test" && inlineSveltePlugin(), sveltekit()],
  // ⬑ only enable during vitest runs in this example – remove the condition to run always
}));

Why conditionally enable? In a typical SvelteKit project you already compile .svelte files. Turning the plugin on just for unit tests keeps production builds untouched while giving Vitest access to inline components.


🧱 Definitions Fence

To share code across multiple inline components in the same file, wrap your ES imports, variables, and "global" component definitions in a special comment block. The plugin will make everything inside this fence available to each inline component's script block.

/* svelte:definitions
  import { tick } from "svelte";

  // Available to all inline components in this file
  const sharedClass = "text-red-500";

  // This component is also globally available
  const GlobalButton = html`<button on:click>Click Me!</button>`;
*/

const Thing1 = html`
  <script>
    let n = $state(0);
  </script>
  <button class="{sharedClass}" onclick={() => n++}>Button: {n}</button>
  <GlobalButton />
`;

---

## 🧩 Named Exports & Snippets

Just like regular Svelte files, you can use `<script context="module">` (or `<script module>`) to export values from an inline component. This is especially useful for **Svelte 5 snippets**.

The plugin makes any named exports available as properties on the component itself.

```tsx
import { html, type InlineSnippet } from "@hvniel/vite-plugin-svelte-inline-component";

const ComponentWithSnippets = html`
  <script lang="ts" module>
    // These snippets will be attached to the component export
    export { header, footer };
  </script>

  {#snippet header(text: string)}
  <header>
    <h1>{text}</h1>
  </header>
  {/snippet} {#snippet footer()}
  <footer>
    <p>&copy; 2025</p>
  </footer>
  {/snippet}
`;

// Now you can render the component and pass snippets to it
const { header, footer } = ComponentWithSnippets as unknown as {
  header: InlineSnippet;
  footer: InlineSnippet;
};

const renderer = render(anchor => {
  header(anchor, () => "Welcome!");
});

Typing Named Exports

To make TypeScript aware of your named exports, you'll need to use a type assertion.

import { html, type InlineSnippet } from "@hvniel/vite-plugin-svelte-inline-component";

const defaultExport = html`
  <script module>
    export { element };
  </script>

  {#snippet element(content)}
  <strong>{content}</strong>
  {/snippet}
`;

// Use `as` to tell TypeScript about the named export
const { element } = defaultExport as unknown as {
  element: InlineSnippet<string>;
};

// `element` is now fully typed!

🧪 Testing Inline & Reactive Components

The plugin is perfect for writing component tests without creating separate .svelte files. It works great with Vitest and testing libraries like @testing-library/svelte or vitest-browser-svelte.

Here’s a sample test that proves reactivity with Svelte 5 runes works out of the box:

import { render } from "vitest-browser-svelte";
import { html, type InlineSnippet } from "@hvniel/vite-plugin-svelte-inline-component";

it("supports reactive components", async () => {
  const ReactiveComponent = html`
    <script>
      let count = $state(0);

      function increment() {
        count++;
      }
    </script>

    <button onclick="{increment}">Count: {count}</button>
  `;

  const { getByRole } = render(ReactiveComponent);
  const button = getByRole("button");

  expect(button).toHaveTextContent("Count: 0");

  await user.click(button);

  expect(button).toHaveTextContent("Count: 1");
});

🛠️ API

inlineSveltePlugin(options?: InlineSvelteOptions): Plugin;

InlineSvelteOptions

| option | type | default | description | | :----------- | :--------- | :---------------------- | :----------------------------------------------- | | tags | string[] | ["html", "svelte"] | Tag names to be treated as inline Svelte markup. | | fenceStart | string | /* svelte:definitions | The comment that starts a standard import fence. | | fenceEnd | string | */ | The comment that ends a standard import fence. |


🧐 How it works (nutshell)

The plugin uses a multi-stage process to transform your code:

  1. Scan for Fences: The plugin first looks for /* svelte:globals */ and /* svelte:imports */ fences to identify shared components and imports.
  2. Process Globals: It compiles any components found inside the globals fence.
  3. Process Locals: It then compiles the remaining "local" components, injecting the standard imports and the compiled global components into each one's script scope.
  4. Replace Literals: Finally, it replaces all the original html\...`` literals in your code with variables that point to the newly created virtual components.

The result behaves just like a normal Svelte component import.


⚠️ Caveats

  • The plugin only transforms your application's source code, ignoring anything inside node_modules.
  • The template content must be valid Svelte component markup. Syntax errors will surface during compilation.
  • Because each inline component gets a unique hash, HMR will re-render the whole component tree containing it. Keep inline components small for best performance.

📝 License

MIT © 2025 Haniel Ubogu