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

@superhighway/silk

v0.3.1

Published

Silk is an embedded DSL for authoring HTML from TypeScript. You write simple typed [JSX][jsx] and Silk generates [`ReadableStream`s][readable-stream] of [`HTMLToken`s][html-tokens].

Readme

silk

Silk is an embedded DSL for authoring HTML from TypeScript. You write simple typed JSX and Silk generates ReadableStreams of HTMLTokens.

Child nodes and attributes can be async values or streams.

Here's an example:

import { createElement, type ReadableHTMLStream } from '@superhighway/silk'

const slowlyGetPlanet = () =>
  // Imagine this queries a database or some third-party API.
  new Promise<ReadableHTMLStream>(resolve =>
    setTimeout(() => resolve(<strong>world</strong>), 2000),
  )

export default () => (
  <html lang="en">
    <head>
      <title>Greeting</title>
    </head>
    <body>Hello, {slowlyGetPlanet()}!</body>
  </html>
)

The HTML structure and content before the slowlyGetPlanet call will immediately be readable from the stream, while the rest will appear as soon as the Promise returned by slowlyGetPlanet resolves.

Setup

To use Silk, add these options to your tsconfig.json[^1]:

"jsx": "react",
"jsxFactory": "createElement",
"jsxFragmentFactory": "createElement",

Also, import { createElement } from '@superhighway/silk' in each of your .tsx files.

Server-Side Usage

If you're using Silk for server-side rendering and want a stream to pipe out as the HTTP response, HTMLSerializingTransformStream has you covered. Here's an example of an HTTP server which uses Silk to serve a web page:

import { createServer } from 'node:http'
import { Writable } from 'node:stream'
import {
  type ReadableHTMLStream,
  createElement,
  HTMLSerializingTransformStream,
} from '@superhighway/silk'

const port = 80

createServer((_request, response) => {
  const document = (
    <html lang="en">
      <head>
        <title>Greeting</title>
      </head>
      <body>Hello, {slowlyGetPlanet()}!</body>
    </html>
  )

  response.setHeader('Content-Type', 'text/html; charset=utf-8')
  document
    .pipeThrough(
      new HTMLSerializingTransformStream({
        includeDoctype: true,
      }),
    )
    .pipeTo(Writable.toWeb(response))
    .catch(console.error)
}).listen(port)

const slowlyGetPlanet = () =>
  new Promise<ReadableHTMLStream>(resolve =>
    setTimeout(() => resolve(<strong>world</strong>), 2000),
  )

If you run that and make a request to it from a web browser, you'll see "Hello, " appear quickly, then "world!" appear after two seconds. You can try it on StackBlitz.

For a somewhat more featureful web server to use with Silk, see Loom, and for more elaborate examples, see https://github.com/mkantor/silk-demos.

Client-Side Usage

Silk can also be used client-side by translating the stream of HTMLTokens into DOM method calls. Silk exports a consumeAsDOMChildren helper function to make this straightforward; here's an example:

import {
  createElement,
  consumeAsDOMChildren,
  type ReadableHTMLStream,
} from '@superhighway/silk'

const slowlyGetPlanet = () =>
  new Promise<ReadableHTMLStream>(resolve =>
    setTimeout(() => resolve(<strong>world</strong>), 2000),
  )

const container = document.getElementById('app')
if (container === null) {
  throw new Error('Container element does not exist')
}

await consumeAsDOMChildren(container, <>Hello, {slowlyGetPlanet()}!</>)

You can try this on StackBlitz.

Why Streaming?

HTML is inherently streamable, yet many web servers buffer the entire response body before sending a single byte of it to the client. This leaves performance on the table—web browsers are perfectly capable of incrementally parsing and rendering partial HTML documents as they arrive.

Streaming is especially valuable when the document references external resources (e.g. stylesheets). By sending HTML to the client while the server continues asynchronous work, the browser can fetch those resources concurrently with that work, significantly reducing the time required to display the page.

Streaming also helps keep server memory utilization low as data already sent to the client can be freed.

It's Just HTML

Attributes

There are no non-HTML attributes (like ref and key), attribute names are always spelled exactly as they are in HTML (e.g. class is not className), and attribute values are plain strings.

Components

Silk doesn't have "components" as part of its JSX syntax—all elements are intrinsic. If you want to abstract/reuse bits of HTML, you can call functions from within {…} blocks:

<div>{profile(userID)}</div>

Typing

JSX elements are strictly-typed, with each element only accepting its known attributes, attributes only accepting known values, and void elements forbidding children. Tag names and attributes are suggested in completions, and documentation from MDN is presented in type info:

Screenshot of type info for a JSX
element

[^1]: "jsx": "react" may seem odd because Silk isn't related to React, but TypeScript's JSX configuration is based around React's semantics.