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

rescript-opentui-react

v0.1.0

Published

ReScript bindings for OpenTUI React - Build terminal user interfaces with React and type-safe functional programming

Readme

ReScript OpenTUI React Bindings

ReScript bindings for OpenTUI React - Build beautiful terminal user interfaces with React and ReScript.

Features

Full Type Safety - Leverages ReScript's powerful type system
React Components - All OpenTUI React components with type-safe props
Hooks Support - Complete bindings for all OpenTUI React hooks
Functional Style - Natural functional programming patterns
Pattern Matching - Use ReScript's pattern matching for event handling
Styled Text - Full support for styled text with proper types

Installation

# Install the package
npm install rescript-opentui-react

# Or with yarn/pnpm/bun
yarn add rescript-opentui-react
pnpm add rescript-opentui-react
bun add rescript-opentui-react

# You also need the peer dependencies
npm install @opentui/core @opentui/react react rescript

Quick Start

// App.res
open OpenTUI

@react.component
let make = () => {
  <Box style={padding: 2, flexDirection: #column}>
    <Text
      content="Hello from ReScript!"
      style={fg: Core.Colors.brightCyan}
    />
  </Box>
}

// Start the app
render(<App />)->ignore

Components

Box

Container component with flexible layout options:

<Box
  title="My Box"
  style={
    border: true,
    borderStyle: #double,
    padding: 2,
    flexDirection: #column,
  }>
  {children}
</Box>

Text

Display styled text:

<Text
  content="Hello World"
  style={
    fg: Core.Colors.green,
    attributes: Core.TextAttributes.bold,
  }
/>

Input

Text input field with event handlers:

<Input
  placeholder="Enter text..."
  value=inputValue
  focused=isFocused
  onInput={value => setInputValue(_ => value)}
  onSubmit={value => handleSubmit(value)}
/>

Select

Dropdown selection component:

let options = [
  {label: "Option 1", value: "1"},
  {label: "Option 2", value: "2"},
]

<Select
  options
  selectedIndex=0
  focused=isFocused
  onChange={(index, option) => handleChange(index, option)}
/>

ScrollBox

Scrollable container:

<ScrollBox
  focused=isFocused
  style={height: 10, overflow: #scroll}>
  {/* Long content */}
</ScrollBox>

ASCIIFont

Large ASCII text rendering:

<ASCIIFont
  text="TITLE"
  font="Standard"
  style={fg: Core.Colors.brightYellow}
/>

Hooks

useKeyboard

Handle keyboard events:

Hooks.useKeyboard(key => {
  switch key.name {
  | Some("up") => moveUp()
  | Some("down") => moveDown()
  | Some("return") => submit()
  | Some("c") when key.ctrl => exit()
  | _ => ()
  }
})

useRenderer

Access the renderer instance:

let renderer = Hooks.useRenderer()

switch renderer {
| Some(r) => // Use renderer
| None => ()
}

useTerminalDimensions

Get terminal size:

let (width, height) = Hooks.useTerminalDimensions()

useResize

Handle terminal resize:

Hooks.useResize((width, height) => {
  Js.log2("Terminal resized to:", (width, height))
})

Custom Hooks

The package includes helpful custom hooks:

// Focus management
let (focusedIndex, isFocused, getFocusedItem) =
  Hooks.useFocusManager(["input1", "input2", "button"])

// Input state management
let (value, handleInput, handleSubmit, clear) =
  Hooks.useInput(~initialValue="", ~onSubmit=submitForm)

// Select state management
let (selectedIndex, handleChange, getSelectedOption) =
  Hooks.useSelect(~options, ~defaultIndex=0)

Styling

Colors

Use predefined colors:

open OpenTUI.Core.Colors

let styles = {
  fg: brightCyan,
  bg: black,
  borderColor: green,
}

Text Attributes

Combine text attributes:

open OpenTUI.Core

let style = {
  attributes: combineAttributes([
    TextAttributes.bold,
    TextAttributes.italic,
    TextAttributes.underline,
  ])
}

Styled Text Functions

Create styled text using the formatting functions:

open OpenTUI.Core

// Simple bold text
<Text content={toStyledText(bold("Bold text"))} />

// Chaining styles
<Text content={
  toStyledText(
    fgChunk(Colors.yellow)(italicChunk(bold("Bold italic yellow")))
  )
} />

// With background color
<Text content={
  toStyledText(
    bgChunk(Colors.blue)(fgChunk(Colors.white)(bold("White on blue")))
  )
} />

// Or use style attributes for simpler cases
<Text 
  content="Styled text"
  style={
    fg: Colors.cyan,
    attributes: combineAttributes([
      TextAttributes.bold,
      TextAttributes.italic,
    ]),
  }
/>

Style Record

Complete style options:

let myStyle: OpenTUI.style = {
  // Colors
  fg: "#00ff00",
  bg: "black",

  // Layout
  width: 40,
  height: 10,
  padding: 2,
  margin: 1,

  // Border
  border: true,
  borderStyle: #double,
  borderColor: "blue",

  // Flexbox
  flexDirection: #row,
  justifyContent: #center,
  alignItems: #center,
  gap: 2,

  // Position
  position: #absolute,
  top: 5,
  left: 10,
}

Examples

Counter App

// examples/Counter.res
@react.component
let make = () => {
  let (count, setCount) = React.useState(() => 0)

  Hooks.useKeyboard(key => {
    switch key.name {
    | Some("up") => setCount(prev => prev + 1)
    | Some("down") => setCount(prev => prev - 1)
    | _ => ()
    }
  })

  <Box>
    <Text content={`Count: ${Int.toString(count)}`} />
  </Box>
}

Login Form

See examples/LoginForm.res for a complete login form implementation with:

  • Multiple input fields
  • Focus management
  • Form validation
  • Status messages

Running Examples

# Build the ReScript code
bun run build

# Run examples
bun run example:basic
bun run example:counter
bun run example:login

Type Definitions

The bindings expose all necessary types:

type style = { /* all style properties */ }
type keyInfo = { name: option<string>, ctrl: bool, alt: bool, shift: bool }
type selectOption = { label: string, value: string, disabled?: bool }
type renderer

Development

# Install dependencies
bun install

# Build ReScript code
bun run build

# Watch mode for development
bun run watch

# Clean build artifacts
bun run clean

Project Structure

packages/rescript/
├── src/
│   ├── OpenTUI.res           # Main module
│   ├── OpenTUI_Types.res     # Type definitions
│   ├── OpenTUI_Core.res      # Core utilities
│   ├── OpenTUI_Components.res # React components
│   └── OpenTUI_Hooks.res     # React hooks
├── examples/
│   ├── Basic.res             # Basic example
│   ├── Counter.res           # Counter app
│   └── LoginForm.res         # Login form
├── package.json
├── rescript.json
└── README.md

Tips

  1. Pattern Matching: Use ReScript's pattern matching for cleaner event handling
  2. Variants: Use polymorphic variants for finite states (focus, status, etc.)
  3. Option Types: Safely handle nullable values with option types
  4. Records: Use record types for complex configuration objects
  5. Pipe Operator: Chain operations with the pipe operator (->)

Known Issues

Bun Runtime Crashes

There are currently known Bun runtime bugs that cause segmentation faults with certain OpenTUI configurations:

  1. flexDirection: #row - Causes Bun to crash. Use #column instead.
  2. borderStyle: #bold - Causes Bun to crash. Use other border styles like #single, #double, or #round.

These issues appear to be bugs in Bun's FFI interaction with OpenTUI's native Zig components. See KNOWN_ISSUES.md for more details.

Select Component

The selectOption type uses name instead of label for the display text:

let options = [
  {name: "Option 1", value: "1", description: "First option"},
  {name: "Option 2", value: "2"},
]

Contributing

Contributions are welcome! Please feel free to submit issues or pull requests.

License

MIT - See the main OpenTUI repository for details.