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

1ch

v0.3.0

Published

Terminal UI components for React

Downloads

194

Readme

1ch

Terminal UI components for React. For those who'd rather be in the terminal.

1ch dashboard example

The smallest unit is one character cell. A button is [ OK ]. A progress bar is ████░░░░. A table border is and and . The whole thing is a grid.

npm install 1ch react react-dom

What it looks like

import { TermUI, TBox, TVStack, TTable, TBar } from "1ch";
import "1ch/style.css";

function Dashboard() {
  return (
    <TermUI width={60}>
      <TBox title="Server Status">
        <TVStack gap={1}>
          <TBar label="CPU" value={73} max={100} />
          <TBar label="MEM" value={4.2} max={8} />
          <TTable
            columns={[
              { key: "service", width: 20 },
              { key: "status", width: 10 },
              { key: "uptime", width: 12 },
            ]}
            data={[
              { service: "api-gateway", status: "UP", uptime: "14d 3h" },
              { service: "worker-pool", status: "UP", uptime: "14d 3h" },
              { service: "cache", status: "WARN", uptime: "2h 41m" },
            ]}
          />
        </TVStack>
      </TBox>
    </TermUI>
  );
}

Why this exists

Most UI components are styled <div>s pretending to be something else. Terminal UIs skip the pretense - every character is either content or structure, and the grid makes boundaries obvious.

Under the hood, 1ch uses a custom React reconciler that turns your component tree into character grids. React handles lifecycle, hooks, state. The reconciler handles layout. Resize the container and everything reflows.

Components

Layout - TVStack, THStack, TBox, TSeparator, TBlank, TLine, TSpan

Data - TTable, TTree, TList, TCode, TUnifiedDiff, TDiffCompute, TBar, TSpark

Interactive - TTabs, TButton, TStatusbar

Documents - TMarkdown, THtml, TJson

The document components accept raw strings. Feed TMarkdown a markdown string, THtml an HTML string, or TJson a JSON object, and you get terminal-styled output back.

A more complete example

import { TermUI, TBox, TCode, TTabs, TTab, TTable, TBar, TVStack } from "1ch";
import "1ch/style.css";

function App() {
  const [tab, setTab] = useState(0);

  return (
    <TermUI width={80}>
      <TTabs active={tab} onSelect={setTab}>
        <TTab name="Overview">
          <TBox title="Status">
            <TVStack>
              <TBar label="CPU" value={73} max={100} />
              <TBar label="MEM" value={4.2} max={8} />
            </TVStack>
          </TBox>
        </TTab>

        <TTab name="Logs">
          <TTable
            columns={[
              { key: "time", width: 12 },
              { key: "level", width: 8 },
              { key: "message", width: 40 },
            ]}
            data={[
              {
                time: "14:03:21",
                level: "INFO",
                message: "Server started on :3000",
              },
              {
                time: "14:03:22",
                level: "INFO",
                message: "Connected to database",
              },
              { time: "14:05:01", level: "WARN", message: "Slow query: 2.3s" },
            ]}
          />
        </TTab>

        <TTab name="Config">
          <TCode
            code={`export default {
  port: 3000,
  database: "postgres://localhost/app",
  cache: { ttl: 300, max: 1000 },
};`}
            language="typescript"
            title="config.ts"
          />
        </TTab>
      </TTabs>
    </TermUI>
  );
}

Don't want React? Skip it.

Every component has an imperative equivalent that returns a layout function. Call it with a width and get a character grid back.

import { box, vstack, table, code, bar } from "1ch";

const layout = vstack(
  box(
    vstack(
      bar(73, 100, 40),
      table(
        [
          { key: "name", header: "Name", width: 20 },
          { key: "value", header: "Value", width: 10 },
        ],
        [
          { name: "requests", value: "1.2k/s" },
          { name: "errors", value: "0.03%" },
        ]
      )
    ),
    { title: "Metrics" }
  ),
  code(`console.log("hello")`, { language: "javascript" })
);

const block = layout(80);

Theming

Dark and light themes built in, and are switchable at runtime. You can override everything via JSON - palette, semantic colors, syntax highlighting, markdown rendering, component tokens. Use parseThemeSpec() to check the JSON before applying it.

import {
  TermThemeProvider,
  TermUI,
  parseThemeSpec,
  defaultThemeSpec,
} from "1ch";

const parsed = parseThemeSpec(userThemeJson);
const spec = parsed.ok ? parsed.theme : defaultThemeSpec;

<TermThemeProvider initialTheme={spec} initialMode="system">
  <TermUI width={80}>{/* components inherit the theme */}</TermUI>
</TermThemeProvider>;

Docs

Full API reference, theme spec details, hooks (useSpinner, useTick, useTermWidth, useStreamingText), and the document pipeline - all in the docs (coming soon).

License

MIT