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

@amazon/vinyl-tsx

v1.1.1

Published

TSX declarative markup for creating UI

Readme

@amazon/vinyl-tsx

Website npm size

A lightweight, reactive JSX runtime for creating dynamic DOM elements with TypeScript support. Provides a declarative way to build user interfaces with reactive data binding via @amazon/vinyl-observable.

Features

  • Reactive properties — bind observable values to DOM properties that update automatically.
  • Hook extensions — enhanced DOM manipulation through custom hooks (style, visible, classList).
  • Lifecycle management — automatic cleanup and connection tracking for DOM elements.
  • TypeScript support — full type safety with utility types for property extraction.
  • Zero dependencies — lightweight runtime with no external framework dependencies.
  • Performance optimized — efficient updates with passive event handlers and selective rendering.

Install

npm install @amazon/vinyl-tsx @amazon/vinyl-util @amazon/vinyl-observable

Basic Usage

Setting up JSX

Configure your TypeScript compiler to use the vinyl-tsx JSX factory:

// tsconfig.json
{
    "compilerOptions": {
        "jsx": "preserve",
        "jsxFactory": "jsx",
        "jsxFragmentFactory": "Fragment"
    }
}

Creating Elements

import { jsx } from '@amazon/vinyl-tsx'

// Basic element creation
const button = <button>Click me</button>

// With properties
const input = <input type="text" placeholder="Enter text" disabled={false} />

// With children
const container = (
    <div className="container">
        <h1>Title</h1>
        <p>Content goes here</p>
    </div>
)

Reactive Properties

Use @amazon/vinyl-observable to create reactive properties that automatically update the DOM:

import { data } from '@amazon/vinyl-observable'
import { jsx } from '@amazon/vinyl-tsx'

const count = data(0)
const isVisible = data(true)

const counter = (
    <div>
        <span>Count: {count}</span>
        <button onclick={() => count.value++}>Increment</button>
        <button visible={isVisible}>Toggle me</button>
    </div>
)

count.value = 5 // DOM updates to show "Count: 5"
isVisible.value = false // Button becomes hidden

Hook Extensions

Style Hook

Apply CSS styles with support for both static and observable values:

import { data } from '@amazon/vinyl-observable'

const color = data('red')
const fontSize = data('16px')

const styledDiv = (
    <div
        style={{
            color, // Observable value
            fontSize, // Observable value
            backgroundColor: 'blue', // Static value
            padding: '10px', // Static value
        }}
    >
        Styled content
    </div>
)

color.value = 'green' // Text color changes to green

Visibility Hook

Control element visibility with the visible property:

const isVisible = data(true)

const conditionalElement = (
    <div visible={isVisible}>This element can be hidden/shown</div>
)

isVisible.value = false // Sets display: none
isVisible.value = true // Removes display property

ClassList Hook

Manage CSS classes dynamically with an array of static strings and/or observable values:

const theme = data<string | null>('dark')

const element = <div classList={['base-class', theme]}>Content</div>

// Element has classes: base-class dark
theme.value = 'light' // Updates to: base-class light
theme.value = null // Updates to: base-class

Connection Hook

Execute code when elements are connected to or disconnected from the DOM:

const element = (
    <div
        onConnect={(el) => {
            console.log('Element connected:', el)
            return () => {
                console.log('Element disconnected:', el)
            }
        }}
    >
        Content
    </div>
)

Event Handlers

Touch and scroll events use passive listeners automatically:

const element = (
    <div
        ontouchstart={(e) => console.log('Touch start')}
        ontouchmove={(e) => console.log('Touch move')}
        onwheel={(e) => console.log('Wheel')}
        onmousedown={(e) => console.log('Mouse down')}
    >
        Interactive content
    </div>
)

Fragments

Use fragments to group elements with a wrapper div using display: contents:

import { Fragment } from '@amazon/vinyl-tsx'

const list = (
    <Fragment>
        <li>Item 1</li>
        <li>Item 2</li>
    </Fragment>
)

const list2 = (
    <>
        <li>Item 1</li>
        <li>Item 2</li>
    </>
)

Custom Components

interface ButtonProps {
    text: string
    onClick: () => void
    variant?: 'primary' | 'secondary'
}

function Button({ text, onClick, variant = 'primary' }: ButtonProps) {
    return (
        <button className={`btn btn-${variant}`} onclick={onClick}>
            {text}
        </button>
    )
}

const myButton = <Button text="Click me" onClick={() => alert('Clicked!')} />

Conditional Rendering

Unlike React, vinyl-tsx does not re-render — JSX expressions are evaluated once at creation time. Use the visible hook to reactively show or hide content:

const showDetails = data(false)

const userCard = (
    <div>
        <h3>John</h3>
        <div visible={showDetails}>
            <p>Email: [email protected]</p>
        </div>
        <button onclick={() => (showDetails.value = !showDetails.value)}>
            Toggle Details
        </button>
    </div>
)

Best Practices

  1. Initialize the connected observer once in your application to enable lifecycle management:

    import { initializeConnectedObserver } from '@amazon/vinyl-tsx'
    
    const cleanup = initializeConnectedObserver()
  2. Use observables for dynamic content — prefer observable values for properties that change over time.

  3. Leverage hook extensionsstyle, visible, and classList cover most common DOM manipulations.

  4. Type safety — let TypeScript check your props and children.

  5. Memory management — observable subscriptions are cleaned up automatically when elements are disconnected.

License

Apache-2.0