Skip to content

Ink/Chalk Compatibility

Silvery provides compatibility layers for both Ink and Chalk, making migration straightforward. This reference documents the complete API mapping.

Compatibility Summary

Test suitePass rateNotes
Ink's own test suite (via bun run compat)804/813 (98.9%)Real Ink tests, Flexily engine
Chalk's own test suite32/32 (100%)Full Chalk API compatibility

Compatibility is tested by cloning the real Ink and Chalk repos and running their original test suites against silvery's compat layer (bun run compat). The 9 remaining Ink test failures are edge cases in the Flexily layout engine (flex-wrap, aspect ratio) and minor compat gaps (overflowX, measure-element, render-to-string). For exact Yoga layout parity, silvery supports Yoga as a pluggable engine. See Silvery vs Ink for the full breakdown.

Ink Compatibility

Import Mapping

diff
- import { Box, Text, render, useInput, useApp } from 'ink'
+ import { Box, Text, render, useInput, useApp } from 'silvery'

- import { render } from 'ink-testing-library'
+ import { render } from '@silvery/test'

Or use the explicit compat layer:

tsx
import { Box, Text, render } from "silvery/ink"

Component Compatibility

ComponentInkSilveryNotes
<Box>SupportedSupportedSame flexbox props
<Text>SupportedSupportedAdded wrap prop
<Newline>SupportedSupportedIdentical
<Spacer>SupportedSupportedIdentical
<Static>SupportedSupportedIdentical
<Transform>SupportedSupportedIdentical

Hook Compatibility

HookInkSilveryNotes
useInput()SupportedSupportedSame callback signature
useApp()SupportedSupportedSame API
useStdout()SupportedSupportedSame API
useFocus()SupportedSupportedEnhanced with spatial navigation
useFocusManager()SupportedSupportedSame API
measureElement()SupportedSupportedWorks, but prefer useContentRect()
useBoxMetrics()--NewPost-layout dimensions

render() Differences

tsx
// Ink
const { unmount, waitUntilExit } = render(<App />)

// Silvery -- just add await
const { unmount, waitUntilExit } = await render(<App />)

// Silvery -- with explicit terminal
using term = createTerm()
const { unmount, waitUntilExit } = await render(<App />, term)

Behavior Differences

BehaviorInkSilveryReason
Default flexDirectioncolumnrowW3C CSS spec compliance
Text overflowOverflows containerWraps by defaultBetter default
First render dimensionsN/A{ width: 0, height: 0 }Required for responsive layout
overflow valuesvisible, hiddenvisible, hidden, scrollScrolling support
Internal APIsExposedHiddenNot public API

Layout engine note: Silvery defaults to Flexily, which follows the W3C CSS spec where Yoga diverges. For exact Ink layout parity (e.g., flexWrap, alignContent, percentage flexBasis), use Yoga as the layout engine. See Flexily vs Yoga Philosophy.

Flexbox Props

All flexbox props work identically:

PropInkSilvery
flexDirectionSupportedSupported
flexGrowSupportedSupported
flexShrinkSupportedSupported
flexBasisSupportedSupported
justifyContentSupportedSupported
alignItemsSupportedSupported
alignSelfSupportedSupported
flexWrapSupportedSupported
width / heightSupportedSupported
minWidth / minHeightSupportedSupported
maxWidth / maxHeightSupportedSupported
padding / paddingX / paddingYSupportedSupported
margin / marginX / marginYSupportedSupported
gapNot supportedSupported

Border Styles

All border styles work identically: single, double, round, bold, classic, arrow, heavy, doubleSingle, singleDouble.

Chalk Compatibility

Import Mapping

diff
- import chalk from 'chalk'
+ import chalk from 'silvery/chalk'

Feature Support

FeatureChalksilvery/chalk
Standard colorschalk.red()Supported
Bright colorschalk.redBright()Supported
Background colorschalk.bgRed()Supported
Modifierschalk.bold(), chalk.dim(), etc.Supported
RGB colorschalk.rgb(255, 0, 0)Supported
Hex colorschalk.hex('#ff0000')Supported
256 colorschalk.ansi256(196)Supported
Chainingchalk.red.bold.underline()Supported
Template literalschalk`{red text}`Supported
chalk.levelColor level detectionSupported

Chalk Strings in Components

Chalk-styled strings work inside Silvery's <Text> component:

tsx
import chalk from "silvery/chalk"
import { Text } from "silvery"

<Text>{chalk.red.bold("Error!")}</Text>
<Text>{chalk.green("Success")} and {chalk.yellow("warning")}</Text>

Compat Layer Architecture

The Ink compatibility layer (@silvery/compat) is built as thin adapters that bridge Ink's APIs to silvery-native systems. Understanding the architecture helps you decide when to use Ink-compat hooks vs silvery-native ones.

How It Works

withInk() composes two independent plugins:

typescript
// withInk() = withInkCursor() + withInkFocus()
const app = pipe(
  createApp(store),
  withReact(<App />),
  withTerminal(process),
  withInk(), // applies both adapters
)
PluginInk APIBridges toSize
withInkCursor()useCursor()silvery CursorStore~50 lines
withInkFocus()useFocus(), useFocusManager()InkFocusProvider (flat list)~45 lines

Error handling is not part of the compat layer — silvery's built-in SilveryErrorBoundary wraps all apps automatically in createApp().

Ink Focus vs Silvery Focus

The two focus systems have fundamentally different designs:

Ink Focus (useFocus)Silvery Focus (useFocusable)
ModelFlat list of component-registered IDsTree of layout nodes with spatial awareness
NavigationTab/Shift+Tab onlyTab, arrow keys (spatial), click-to-focus
ScopingNone — all focusables in one global listFocus scopes isolate regions (e.g., modal dialogs)
RegistrationComponents call add(id) / remove(id)Automatic from layout tree
EventsNoneDOM-style focus/blur with capture/target/bubble

For new code, use silvery's focus system. The Ink compat focus exists for apps migrating from Ink that already use useFocus() / useFocusManager().

Ink Cursor vs Silvery Cursor

Both systems write to the same CursorStorewithInkCursor() just provides the InkCursorStoreCtx context that Ink's useCursor() hook reads from. The underlying cursor mechanism is identical.

Gradual Migration Path

You can incrementally drop Ink adapters as you adopt silvery-native APIs:

  1. Start: withInk() — everything works like Ink
  2. Replace focus: Switch from useFocus() to useFocusable(), drop withInkFocus()
  3. Replace cursor: Switch from Ink's useCursor() to silvery's useCursor(), drop withInkCursor()
  4. Done: No compat layer needed

Silvery-Only Features

These features have no Ink/Chalk equivalent:

FeatureAPI
Layout feedbackuseContentRect()
Scrollable containersoverflow="scroll" + scrollTo
Input layer isolation<InputLayerProvider>
Spatial focus<FocusScope> with arrow keys
Mouse eventsonClick, onMouseDown, onWheel
Command systemwithCommands()
Theme tokenscolor="$primary"
TextAreaBuilt-in multi-line editing
30+ componentsVirtualList, Table, ModalDialog, etc.

See Also

Released under the MIT License.