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

@svebcomponents/auto-options

v0.2.0

Published

Infer Svelte custom element prop options from `$props()`.

Readme

Infer Svelte custom element prop options from $props().

Svelte custom elements need prop metadata to expose component props as HTML attributes. Writing that metadata by hand gets repetitive, especially when the same information already exists in your TypeScript props.

@svebcomponents/auto-options is a build plugin that reads a Svelte component's instance script, infers prop names and primitive prop types from $props(), and injects or updates <svelte:options customElement={...} />.

Example

<script lang="ts">
  interface Props {
    favoriteNumber: number;
  }

  let props: Props = $props();
</script>

<h1>Favorite number: {props.favoriteNumber}</h1>

is transformed before the Svelte compiler runs:

<svelte:options
  customElement={{
    props: {
      favoriteNumber: {
        attribute: "favorite-number",
        reflect: true,
        type: "Number",
      },
    },
  }}
/>

<script lang="ts">
  interface Props {
    favoriteNumber: number;
  }

  let props: Props = $props();
</script>

<h1>Favorite number: {props.favoriteNumber}</h1>

The generated attribute name is kebab-cased, so consumers can use:

<favorite-number favorite-number="42"></favorite-number>

Usage

With @svebcomponents/build

If you build with @svebcomponents/build, no extra setup is needed. The generated tsdown config already runs @svebcomponents/auto-options before compiling Svelte.

Manual Usage

Install the package:

pnpm add -D @svebcomponents/auto-options

Add the plugin before the Svelte plugin in your Vite config:

import autoOptions from "@svebcomponents/auto-options";
import { svelte } from "@sveltejs/vite-plugin-svelte";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    autoOptions(),
    svelte({
      compilerOptions: {
        customElement: true,
      },
    }),
  ],
});

Then expose the compiled custom element from your package entrypoint. Outside @svebcomponents/build's pipeline you don't get its automatic registration guard, so guard it by hand:

import Component from "./Component.svelte";

if (!customElements.get("favorite-number") && Component.element) {
  customElements.define("favorite-number", Component.element);
}

export default Component;

auto-options generates the prop metadata Svelte needs for attribute conversion, but the component still has to be compiled and registered as a custom element. Without that custom element output, attributes such as favorite-number="42" will stay strings.

Declare the tag directly in the component with the string-shorthand form, and auto-options expands it into the object form with the inferred props merged in:

<svelte:options customElement="favorite-number" />

The object form works too — use it directly when you also need shadow or a manual extend:

<svelte:options
  customElement={{
    tag: "favorite-number",
  }}
/>

What Gets Inferred

The plugin looks for a variable declaration initialized from $props() in the component instance script.

<script lang="ts">
  let props: Props = $props();
</script>

It can infer prop names and custom element types from:

| Svelte prop type | Generated custom element type | | -------------------------- | -------------------------------------- | | string | "String" | | number | "Number" | | boolean | "Boolean" | | string/number/bool literal | "String", "Number", or "Boolean" | | SomeType[] | "Array" | | Array<SomeType> | "Array" | | object type literals | "Object" | | Record<...> | "Object" | | interface references | "Object" |

Props without TypeScript type information are still added, but default to "String" because HTML attributes are strings by default.

Supported Prop Shapes

Inline prop types:

<script lang="ts">
  let props: { count: number } = $props();
</script>

Type aliases:

<script lang="ts">
  type Props = {
    count: number;
  };

  let props: Props = $props();
</script>

Interfaces:

<script lang="ts">
  interface Props {
    count: number;
  }

  let props: Props = $props();
</script>

Destructured props:

<script lang="ts">
  let { count, ...rest }: { count: number } = $props();
</script>

Untyped destructured props:

<script>
  let { label } = $props();
</script>

Existing Options

Manual custom element options are treated as the highest-priority source of truth.

<svelte:options
  customElement={{
    props: {
      count: { type: "String", attribute: "data-count" },
    },
  }}
/>

If the plugin later infers count as a number, the existing type and attribute values are preserved. Missing fields and newly discovered props are still filled in.

Use this as the escape hatch when inference is wrong or incomplete. You can manually define one prop, several props, or the entire props object; the plugin will preserve the fields you wrote and infer the rest where it can.

Defaults

For every inferred prop, the plugin generates:

  • attribute: the kebab-cased prop name
  • reflect: true
  • type: the inferred Svelte custom element type, or "String" when no type can be resolved

Current Limitations

  • Only Svelte 5 $props() declarations are inspected.
  • The bare customElement boolean shorthand, and a dynamically-interpolated string tag (e.g. customElement="{x}"), aren't supported — use a plain string literal tag or the object form.
  • Imported prop types are not resolved. Type aliases and interfaces must be declared in the same component instance script to be inspected.
  • Generic and complex TypeScript types are not fully resolved. Unknown types fall back to "String" unless they are interface references, which are treated as "Object".