UNPKG

42.3 kBMarkdownView Raw
1<h1 align="center">
2 <br>
3 <br>
4 <img width="200" alt="Ink" src="media/logo.png">
5 <br>
6 <br>
7 <br>
8</h1>
9
10> React for CLIs. Build and test your CLI output using components.
11
12[![Build Status](https://github.com/vadimdemedes/ink/workflows/test/badge.svg)](https://github.com/vadimdemedes/ink/actions)
13[![npm](https://img.shields.io/npm/dm/ink?logo=npm)](https://npmjs.com/package/ink)
14
15Ink provides the same component-based UI building experience that React offers in the browser, but for command-line apps.
16It uses [Yoga](https://github.com/facebook/yoga) to build Flexbox layouts in the terminal, so most CSS-like props are available in Ink as well.
17If you are already familiar with React, you already know Ink.
18
19Since Ink is a React renderer, it means that all features of React are supported.
20Head over to [React](https://reactjs.org) website for documentation on how to use it.
21Only Ink's methods will be documented in this readme.
22
23**Note:** This is documentation for Ink 3. If you're looking for docs on Ink 2, check out [this release](https://github.com/vadimdemedes/ink/tree/v2.7.1). There's also a [migration guide](migrate.md) from Ink 2 available.
24
25## Install
26
27```
28$ npm install ink react
29```
30
31## Usage
32
33```jsx
34import React, {useState, useEffect} from 'react';
35import {render, Text} from 'ink';
36
37const Counter = () => {
38 const [counter, setCounter] = useState(0);
39
40 useEffect(() => {
41 const timer = setInterval(() => {
42 setCounter(previousCounter => previousCounter + 1);
43 }, 100);
44
45 return () => {
46 clearInterval(timer);
47 };
48 }, []);
49
50 return <Text color="green">{counter} tests passed</Text>;
51};
52
53render(<Counter />);
54```
55
56<img src="media/demo.svg" width="600">
57
58You can also check it out live on [repl.it sandbox](https://ink-counter-demo.vadimdemedes.repl.run/).
59Feel free to play around with the code and fork this repl at [https://repl.it/@vadimdemedes/ink-counter-demo](https://repl.it/@vadimdemedes/ink-counter-demo).
60
61## Who's Using Ink?
62
63- [Gatsby](https://www.gatsbyjs.org) - Gatsby is a modern web framework for blazing fast websites.
64- [tap](https://node-tap.org) - A Test-Anything-Protocol library for JavaScript.
65- [Terraform CDK](https://github.com/hashicorp/terraform-cdk) - CDK (Cloud Development Kit) for HashiCorp Terraform.
66- [Twilio's SIGNAL](https://github.com/twilio-labs/plugin-signal2020) - CLI for Twilio's SIGNAL conference. [Blog post](https://www.twilio.com/blog/building-conference-cli-in-react).
67- [Typewriter](https://github.com/segmentio/typewriter) - Generates strongly-typed [Segment](https://segment.com) analytics clients from arbitrary JSON Schema.
68- [Prisma](https://www.prisma.io) - The unified data layer for modern applications.
69- [Wallace](https://www.projectwallace.com) - Pretty CSS analytics on the CLI.
70- [Blitz](https://blitzjs.com) - The Fullstack React Framework.
71- [New York Times](https://github.com/nytimes/kyt) - NYT uses Ink `kyt` - a toolkit that encapsulates and manages the configuration for web apps.
72- [tink](https://github.com/npm/tink) - Next-generation runtime and package manager.
73- [loki](https://github.com/oblador/loki) - Visual Regression Testing for Storybook.
74- [Bit](https://github.com/teambit/bit) - Build, distribute and collaborate on components.
75- [Remirror](https://github.com/remirror/remirror) - Your friendly, world-class editor toolkit.
76- [Prime](https://github.com/birkir/prime) - Open source GraphQL CMS.
77- [Splash](https://github.com/Shopify/polaris-react/tree/master/scripts/splash) - Observe the splash zone of a change across the Shopify's [Polaris](https://polaris.shopify.com) component library.
78- [emoj](https://github.com/sindresorhus/emoj) - Find relevant emoji on the command-line.
79- [emma](https://github.com/maticzav/emma-cli) - Terminal assistant to find and install npm packages.
80- [swiff](https://github.com/simple-integrated-marketing/swiff) - Multi-environment command line tools for time-saving web developers.
81- [share](https://github.com/marionebl/share-cli) - Quickly share files from your command line.
82- [Kubelive](https://github.com/ameerthehacker/kubelive) - CLI for Kubernetes to provide live data about the cluster and its resources.
83- [changelog-view](https://github.com/jdeniau/changelog-view) - Tool view changelog in console.
84- [cfpush](https://github.com/mamachanko/cfpush) - An interactive Cloud Foundry tutorial in your terminal.
85- [startd](https://github.com/mgrip/startd) - Turn your React component into a web app from the command-line.
86- [wiki-cli](https://github.com/hexrcs/wiki-cli) - Search Wikipedia and read summaries directly in your terminal.
87- [garson](https://github.com/goliney/garson) - Build interactive config-based command-line interfaces.
88- [git-contrib-calendar](https://github.com/giannisp/git-contrib-calendar) - Display a contributions calendar for any git repository.
89- [gitgud](https://github.com/GitGud-org/GitGud) - An interactive command-line GUI for Git.
90- [Autarky](https://github.com/pranshuchittora/autarky) - An interactive CLI to find and delete old `node_modules` directories in order to free up disk space.
91- [fast-cli](https://github.com/sindresorhus/fast-cli) - Test your download and upload speed.
92- [tasuku](https://github.com/privatenumber/tasuku) - Minimal task runner.
93
94## Contents
95
96- [Getting Started](#getting-started)
97- [Components](#components)
98 - [`<Text>`](#text)
99 - [`<Box>`](#box)
100 - [`<Newline>`](#newline)
101 - [`<Spacer>`](#spacer)
102 - [`<Static>`](#static)
103 - [`<Transform>`](#transform)
104- [Hooks](#hooks)
105 - [`useInput`](#useinputinputhandler-options)
106 - [`useApp`](#useapp)
107 - [`useStdin`](#usestdin)
108 - [`useStdout`](#usestdout)
109 - [`useStderr`](#usestderr)
110 - [`useFocus`](#usefocusoptions)
111 - [`useFocusManager`](#usefocusmanager)
112- [API](#api)
113- [Testing](#testing)
114- [Using React Devtools](#using-react-devtools)
115- [Useful Components](#useful-components)
116- [Useful Hooks](#useful-hooks)
117- [Examples](#examples)
118
119## Getting Started
120
121Use [create-ink-app](https://github.com/vadimdemedes/create-ink-app) to quickly scaffold a new Ink-based CLI.
122
123```
124$ mkdir my-ink-cli
125$ cd my-ink-cli
126$ npx create-ink-app
127```
128
129Alternatively, create a TypeScript project:
130
131```
132$ npx create-ink-app --typescript
133```
134
135<details><summary>Manual setup</summary>
136<p>
137Ink requires the same Babel setup as you would do for regular React-based apps in the browser.
138
139Set up Babel with a React preset to ensure all examples in this readme work as expected.
140After [installing Babel](https://babeljs.io/docs/en/usage), install `@babel/preset-react` and insert the following configuration in `babel.config.json`:
141
142```
143$ npm install --save-dev @babel/preset-react
144```
145
146```json
147{
148 "presets": [
149 "@babel/preset-react",
150 [
151 "@babel/preset-env",
152 {
153 "targets": {
154 "node": true
155 }
156 }
157 ]
158 ]
159}
160```
161
162Next, create a file `source.js`, where you'll type code that uses Ink:
163
164```jsx
165import React from 'react';
166import {render, Text} from 'ink';
167
168const Demo = () => <Text>Hello World</Text>;
169
170render(<Demo />);
171```
172
173Then, transpile this file with Babel:
174
175```
176$ npx babel source.js -o cli.js
177```
178
179Now you can run `cli.js` with Node.js:
180
181```
182$ node cli
183```
184
185If you don't like transpiling files during development, you can use [import-jsx](https://github.com/vadimdemedes/import-jsx) to `require()` a JSX file and transpile it on the fly.
186
187</p>
188</details>
189
190Ink uses [Yoga](https://github.com/facebook/yoga) - a Flexbox layout engine to build great user interfaces for your CLIs using familiar CSS-like props you've used when building apps for the browser.
191It's important to remember that each element is a Flexbox container.
192Think of it as if each `<div>` in the browser had `display: flex`.
193See [`<Box>`](#box) built-in component below for documentation on how to use Flexbox layouts in Ink.
194Note that all text must be wrapped in a [`<Text>`](#text) component.
195
196## Components
197
198### `<Text>`
199
200This component can display text, and change its style to make it bold, underline, italic or strikethrough.
201
202```jsx
203import {render, Text} from 'ink';
204
205const Example = () => (
206 <>
207 <Text color="green">I am green</Text>
208 <Text color="black" backgroundColor="white">
209 I am black on white
210 </Text>
211 <Text color="#ffffff">I am white</Text>
212 <Text bold>I am bold</Text>
213 <Text italic>I am italic</Text>
214 <Text underline>I am underline</Text>
215 <Text strikethrough>I am strikethrough</Text>
216 <Text inverse>I am inversed</Text>
217 </>
218);
219
220render(<Example />);
221```
222
223**Note:** `<Text>` allows only text nodes and nested `<Text>` components inside of it. For example, `<Box>` component can't be used inside `<Text>`.
224
225#### color
226
227Type: `string`
228
229Change text color.
230Ink uses [chalk](https://github.com/chalk/chalk) under the hood, so all its functionality is supported.
231
232```jsx
233<Text color="green">Green</Text>
234<Text color="#005cc5">Blue</Text>
235<Text color="rgb(232, 131, 136)">Red</Text>
236```
237
238<img src="media/text-color.jpg" width="247">
239
240#### backgroundColor
241
242Type: `string`
243
244Same as `color` above, but for background.
245
246```jsx
247<Text backgroundColor="green" color="white">Green</Text>
248<Text backgroundColor="#005cc5" color="white">Blue</Text>
249<Text backgroundColor="rgb(232, 131, 136)" color="white">Red</Text>
250```
251
252<img src="media/text-backgroundColor.jpg" width="226">
253
254#### dimColor
255
256Type: `boolean`\
257Default: `false`
258
259Dim the color (emit a small amount of light).
260
261```jsx
262<Text color="red" dimColor>
263 Dimmed Red
264</Text>
265```
266
267<img src="media/text-dimColor.jpg" width="138">
268
269#### bold
270
271Type: `boolean`\
272Default: `false`
273
274Make the text bold.
275
276#### italic
277
278Type: `boolean`\
279Default: `false`
280
281Make the text italic.
282
283#### underline
284
285Type: `boolean`\
286Default: `false`
287
288Make the text underlined.
289
290#### strikethrough
291
292Type: `boolean`\
293Default: `false`
294
295Make the text crossed with a line.
296
297#### inverse
298
299Type: `boolean`\
300Default: `false`
301
302Inverse background and foreground colors.
303
304```jsx
305<Text inverse color="yellow">
306 Inversed Yellow
307</Text>
308```
309
310<img src="media/text-inverse.jpg" width="138">
311
312#### wrap
313
314Type: `string`\
315Allowed values: `wrap` `truncate` `truncate-start` `truncate-middle` `truncate-end`\
316Default: `wrap`
317
318This property tells Ink to wrap or truncate text if its width is larger than container.
319If `wrap` is passed (by default), Ink will wrap text and split it into multiple lines.
320If `truncate-*` is passed, Ink will truncate text instead, which will result in one line of text with the rest cut off.
321
322```jsx
323<Box width={7}>
324 <Text>Hello World</Text>
325</Box>
326//=> 'Hello\nWorld'
327
328// `truncate` is an alias to `truncate-end`
329<Box width={7}>
330 <Text wrap="truncate">Hello World</Text>
331</Box>
332//=> 'Hello…'
333
334<Box width={7}>
335 <Text wrap="truncate-middle">Hello World</Text>
336</Box>
337//=> 'He…ld'
338
339<Box width={7}>
340 <Text wrap="truncate-start">Hello World</Text>
341</Box>
342//=> '…World'
343```
344
345### `<Box>`
346
347`<Box>` is an essential Ink component to build your layout.
348It's like `<div style="display: flex">` in the browser.
349
350```jsx
351import {render, Box, Text} from 'ink';
352
353const Example = () => (
354 <Box margin={2}>
355 <Text>This is a box with margin</Text>
356 </Box>
357);
358
359render(<Example />);
360```
361
362#### Dimensions
363
364##### width
365
366Type: `number` `string`
367
368Width of the element in spaces.
369You can also set it in percent, which will calculate the width based on the width of parent element.
370
371```jsx
372<Box width={4}>
373 <Text>X</Text>
374</Box>
375//=> 'X '
376```
377
378```jsx
379<Box width={10}>
380 <Box width="50%">
381 <Text>X</Text>
382 </Box>
383 <Text>Y</Text>
384</Box>
385//=> 'X Y'
386```
387
388##### height
389
390Type: `number` `string`
391
392Height of the element in lines (rows).
393You can also set it in percent, which will calculate the height based on the height of parent element.
394
395```jsx
396<Box height={4}>
397 <Text>X</Text>
398</Box>
399//=> 'X\n\n\n'
400```
401
402```jsx
403<Box height={6} flexDirection="column">
404 <Box height="50%">
405 <Text>X</Text>
406 </Box>
407 <Text>Y</Text>
408</Box>
409//=> 'X\n\n\nY\n\n'
410```
411
412##### minWidth
413
414Type: `number`
415
416Sets a minimum width of the element.
417Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.
418
419##### minHeight
420
421Type: `number`
422
423Sets a minimum height of the element.
424Percentages aren't supported yet, see https://github.com/facebook/yoga/issues/872.
425
426#### Padding
427
428##### paddingTop
429
430Type: `number`\
431Default: `0`
432
433Top padding.
434
435##### paddingBottom
436
437Type: `number`\
438Default: `0`
439
440Bottom padding.
441
442##### paddingLeft
443
444Type: `number`\
445Default: `0`
446
447Left padding.
448
449##### paddingRight
450
451Type: `number`\
452Default: `0`
453
454Right padding.
455
456##### paddingX
457
458Type: `number`\
459Default: `0`
460
461Horizontal padding. Equivalent to setting `paddingLeft` and `paddingRight`.
462
463##### paddingY
464
465Type: `number`\
466Default: `0`
467
468Vertical padding. Equivalent to setting `paddingTop` and `paddingBottom`.
469
470##### padding
471
472Type: `number`\
473Default: `0`
474
475Padding on all sides. Equivalent to setting `paddingTop`, `paddingBottom`, `paddingLeft` and `paddingRight`.
476
477```jsx
478<Box paddingTop={2}>Top</Box>
479<Box paddingBottom={2}>Bottom</Box>
480<Box paddingLeft={2}>Left</Box>
481<Box paddingRight={2}>Right</Box>
482<Box paddingX={2}>Left and right</Box>
483<Box paddingY={2}>Top and bottom</Box>
484<Box padding={2}>Top, bottom, left and right</Box>
485```
486
487#### Margin
488
489##### marginTop
490
491Type: `number`\
492Default: `0`
493
494Top margin.
495
496##### marginBottom
497
498Type: `number`\
499Default: `0`
500
501Bottom margin.
502
503##### marginLeft
504
505Type: `number`\
506Default: `0`
507
508Left margin.
509
510##### marginRight
511
512Type: `number`\
513Default: `0`
514
515Right margin.
516
517##### marginX
518
519Type: `number`\
520Default: `0`
521
522Horizontal margin. Equivalent to setting `marginLeft` and `marginRight`.
523
524##### marginY
525
526Type: `number`\
527Default: `0`
528
529Vertical margin. Equivalent to setting `marginTop` and `marginBottom`.
530
531##### margin
532
533Type: `number`\
534Default: `0`
535
536Margin on all sides. Equivalent to setting `marginTop`, `marginBottom`, `marginLeft` and `marginRight`.
537
538```jsx
539<Box marginTop={2}>Top</Box>
540<Box marginBottom={2}>Bottom</Box>
541<Box marginLeft={2}>Left</Box>
542<Box marginRight={2}>Right</Box>
543<Box marginX={2}>Left and right</Box>
544<Box marginY={2}>Top and bottom</Box>
545<Box margin={2}>Top, bottom, left and right</Box>
546```
547
548#### Flex
549
550##### flexGrow
551
552Type: `number`\
553Default: `0`
554
555See [flex-grow](https://css-tricks.com/almanac/properties/f/flex-grow/).
556
557```jsx
558<Box>
559 <Text>Label:</Text>
560 <Box flexGrow={1}>
561 <Text>Fills all remaining space</Text>
562 </Box>
563</Box>
564```
565
566##### flexShrink
567
568Type: `number`\
569Default: `1`
570
571See [flex-shrink](https://css-tricks.com/almanac/properties/f/flex-shrink/).
572
573```jsx
574<Box width={20}>
575 <Box flexShrink={2} width={10}>
576 <Text>Will be 1/4</Text>
577 </Box>
578 <Box width={10}>
579 <Text>Will be 3/4</Text>
580 </Box>
581</Box>
582```
583
584##### flexBasis
585
586Type: `number` `string`
587
588See [flex-basis](https://css-tricks.com/almanac/properties/f/flex-basis/).
589
590```jsx
591<Box width={6}>
592 <Box flexBasis={3}>
593 <Text>X</Text>
594 </Box>
595 <Text>Y</Text>
596</Box>
597//=> 'X Y'
598```
599
600```jsx
601<Box width={6}>
602 <Box flexBasis="50%">
603 <Text>X</Text>
604 </Box>
605 <Text>Y</Text>
606</Box>
607//=> 'X Y'
608```
609
610##### flexDirection
611
612Type: `string`\
613Allowed values: `row` `row-reverse` `column` `column-reverse`
614
615See [flex-direction](https://css-tricks.com/almanac/properties/f/flex-direction/).
616
617```jsx
618<Box>
619 <Box marginRight={1}>
620 <Text>X</Text>
621 </Box>
622 <Text>Y</Text>
623</Box>
624// X Y
625
626<Box flexDirection="row-reverse">
627 <Text>X</Text>
628 <Box marginRight={1}>
629 <Text>Y</Text>
630 </Box>
631</Box>
632// Y X
633
634<Box flexDirection="column">
635 <Text>X</Text>
636 <Text>Y</Text>
637</Box>
638// X
639// Y
640
641<Box flexDirection="column-reverse">
642 <Text>X</Text>
643 <Text>Y</Text>
644</Box>
645// Y
646// X
647```
648
649##### alignItems
650
651Type: `string`\
652Allowed values: `flex-start` `center` `flex-end`
653
654See [align-items](https://css-tricks.com/almanac/properties/a/align-items/).
655
656```jsx
657<Box alignItems="flex-start">
658 <Box marginRight={1}>
659 <Text>X</Text>
660 </Box>
661 <Text>
662 A
663 <Newline/>
664 B
665 <Newline/>
666 C
667 </Text>
668</Box>
669// X A
670// B
671// C
672
673<Box alignItems="center">
674 <Box marginRight={1}>
675 <Text>X</Text>
676 </Box>
677 <Text>
678 A
679 <Newline/>
680 B
681 <Newline/>
682 C
683 </Text>
684</Box>
685// A
686// X B
687// C
688
689<Box alignItems="flex-end">
690 <Box marginRight={1}>
691 <Text>X</Text>
692 </Box>
693 <Text>
694 A
695 <Newline/>
696 B
697 <Newline/>
698 C
699 </Text>
700</Box>
701// A
702// B
703// X C
704```
705
706##### alignSelf
707
708Type: `string`\
709Default: `auto`\
710Allowed vales: `auto` `flex-start` `center` `flex-end`
711
712See [align-self](https://css-tricks.com/almanac/properties/a/align-self/).
713
714```jsx
715<Box height={3}>
716 <Box alignSelf="flex-start">
717 <Text>X</Text>
718 </Box>
719</Box>
720// X
721//
722//
723
724<Box height={3}>
725 <Box alignSelf="center">
726 <Text>X</Text>
727 </Box>
728</Box>
729//
730// X
731//
732
733<Box height={3}>
734 <Box alignSelf="flex-end">
735 <Text>X</Text>
736 </Box>
737</Box>
738//
739//
740// X
741```
742
743##### justifyContent
744
745Type: `string`\
746Allowed values: `flex-start` `center` `flex-end` `space-between` `space-around`
747
748See [justify-content](https://css-tricks.com/almanac/properties/j/justify-content/).
749
750```jsx
751<Box justifyContent="flex-start">
752 <Text>X</Text>
753</Box>
754// [X ]
755
756<Box justifyContent="center">
757 <Text>X</Text>
758</Box>
759// [ X ]
760
761<Box justifyContent="flex-end">
762 <Text>X</Text>
763</Box>
764// [ X]
765
766<Box justifyContent="space-between">
767 <Text>X</Text>
768 <Text>Y</Text>
769</Box>
770// [X Y]
771
772<Box justifyContent="space-around">
773 <Text>X</Text>
774 <Text>Y</Text>
775</Box>
776// [ X Y ]
777```
778
779#### Visibility
780
781##### display
782
783Type: `string`\
784Allowed values: `flex` `none`\
785Default: `flex`
786
787Set this property to `none` to hide the element.
788
789#### Borders
790
791##### borderStyle
792
793Type: `string`\
794Allowed values: `single` `double` `round` `bold` `singleDouble` `doubleSingle` `classic`
795
796Add a border with a specified style.
797If `borderStyle` is `undefined` (which it is by default), no border will be added.
798Ink uses border styles from [`cli-boxes`](https://github.com/sindresorhus/cli-boxes) module.
799
800```jsx
801<Box flexDirection="column">
802 <Box>
803 <Box borderStyle="single" marginRight={2}>
804 <Text>single</Text>
805 </Box>
806
807 <Box borderStyle="double" marginRight={2}>
808 <Text>double</Text>
809 </Box>
810
811 <Box borderStyle="round" marginRight={2}>
812 <Text>round</Text>
813 </Box>
814
815 <Box borderStyle="bold">
816 <Text>bold</Text>
817 </Box>
818 </Box>
819
820 <Box marginTop={1}>
821 <Box borderStyle="singleDouble" marginRight={2}>
822 <Text>singleDouble</Text>
823 </Box>
824
825 <Box borderStyle="doubleSingle" marginRight={2}>
826 <Text>doubleSingle</Text>
827 </Box>
828
829 <Box borderStyle="classic">
830 <Text>classic</Text>
831 </Box>
832 </Box>
833</Box>
834```
835
836<img src="media/box-borderStyle.jpg" width="521">
837
838See example in [examples/borders](examples/borders/borders.js).
839
840##### borderColor
841
842Type: `string`
843
844Change border color.
845Accepts the same values as [`color`](#color) in `<Text>` component.
846
847```jsx
848<Box borderStyle="round" borderColor="green">
849 <Text>Green Rounded Box</Text>
850</Box>
851```
852
853<img src="media/box-borderColor.jpg" width="228">
854
855### `<Newline>`
856
857Adds one or more newline (`\n`) characters.
858Must be used within `<Text>` components.
859
860#### count
861
862Type: `number`\
863Default: `1`
864
865Number of newlines to insert.
866
867```jsx
868import {render, Text, Newline} from 'ink';
869
870const Example = () => (
871 <Text>
872 <Text color="green">Hello</Text>
873 <Newline />
874 <Text color="red">World</Text>
875 </Text>
876);
877
878render(<Example />);
879```
880
881Output:
882
883```
884Hello
885World
886```
887
888### `<Spacer>`
889
890A flexible space that expands along the major axis of its containing layout.
891It's useful as a shortcut for filling all the available spaces between elements.
892
893For example, using `<Spacer>` in a `<Box>` with default flex direction (`row`) will position "Left" on the left side and will push "Right" to the right side.
894
895```jsx
896import {render, Box, Text, Spacer} from 'ink';
897
898const Example = () => (
899 <Box>
900 <Text>Left</Text>
901 <Spacer />
902 <Text>Right</Text>
903 </Box>
904);
905
906render(<Example />);
907```
908
909In a vertical flex direction (`column`), it will position "Top" to the top of the container and push "Bottom" to the bottom of it.
910Note, that container needs to be tall to enough to see this in effect.
911
912```jsx
913import {render, Box, Text, Spacer} from 'ink';
914
915const Example = () => (
916 <Box flexDirection="column" height={10}>
917 <Text>Top</Text>
918 <Spacer />
919 <Text>Bottom</Text>
920 </Box>
921);
922
923render(<Example />);
924```
925
926### `<Static>`
927
928`<Static>` component permanently renders its output above everything else.
929It's useful for displaying activity like completed tasks or logs - things that
930are not changing after they're rendered (hence the name "Static").
931
932It's preferred to use `<Static>` for use cases like these, when you can't know
933or control the amount of items that need to be rendered.
934
935For example, [Tap](https://github.com/tapjs/node-tap) uses `<Static>` to display
936a list of completed tests. [Gatsby](https://github.com/gatsbyjs/gatsby) uses it
937to display a list of generated pages, while still displaying a live progress bar.
938
939```jsx
940import React, {useState, useEffect} from 'react';
941import {render, Static, Box, Text} from 'ink';
942
943const Example = () => {
944 const [tests, setTests] = useState([]);
945
946 useEffect(() => {
947 let completedTests = 0;
948 let timer;
949
950 const run = () => {
951 // Fake 10 completed tests
952 if (completedTests++ < 10) {
953 setTests(previousTests => [
954 ...previousTests,
955 {
956 id: previousTests.length,
957 title: `Test #${previousTests.length + 1}`
958 }
959 ]);
960
961 setTimeout(run, 100);
962 }
963 };
964
965 run();
966
967 return () => {
968 clearTimeout(timer);
969 };
970 }, []);
971
972 return (
973 <>
974 {/* This part will be rendered once to the terminal */}
975 <Static items={tests}>
976 {test => (
977 <Box key={test.id}>
978 <Text color="green">✔ {test.title}</Text>
979 </Box>
980 )}
981 </Static>
982
983 {/* This part keeps updating as state changes */}
984 <Box marginTop={1}>
985 <Text dimColor>Completed tests: {tests.length}</Text>
986 </Box>
987 </>
988 );
989};
990
991render(<Example />);
992```
993
994**Note:** `<Static>` only renders new items in `items` prop and ignores items
995that were previously rendered. This means that when you add new items to `items`
996array, changes you make to previous items will not trigger a rerender.
997
998See [examples/static](examples/static/static.js) for an example usage of `<Static>` component.
999
1000#### items
1001
1002Type: `Array`
1003
1004Array of items of any type to render using a function you pass as a component child.
1005
1006#### style
1007
1008Type: `object`
1009
1010Styles to apply to a container of child elements.
1011See [`<Box>`](#box) for supported properties.
1012
1013```jsx
1014<Static items={...} style={{padding: 1}}>
1015 {...}
1016</Static>
1017```
1018
1019#### children(item)
1020
1021Type: `Function`
1022
1023Function that is called to render every item in `items` array.
1024First argument is an item itself and second argument is index of that item in
1025`items` array.
1026
1027Note that `key` must be assigned to the root component.
1028
1029```jsx
1030<Static items={['a', 'b', 'c']}>
1031 {(item, index) => {
1032 // This function is called for every item in ['a', 'b', 'c']
1033 // `item` is 'a', 'b', 'c'
1034 // `index` is 0, 1, 2
1035 return (
1036 <Box key={index}>
1037 <Text>Item: {item}</Text>
1038 </Box>
1039 );
1040 }}
1041</Static>
1042```
1043
1044### `<Transform>`
1045
1046Transform a string representation of React components before they are written to output.
1047For example, you might want to apply a [gradient to text](https://github.com/sindresorhus/ink-gradient), [add a clickable link](https://github.com/sindresorhus/ink-link) or [create some text effects](https://github.com/sindresorhus/ink-big-text).
1048These use cases can't accept React nodes as input, they are expecting a string.
1049That's what `<Transform>` component does, it gives you an output string of its child components and lets you transform it in any way.
1050
1051**Note:** `<Transform>` must be applied only to `<Text>` children components and shouldn't change the dimensions of the output, otherwise layout will be incorrect.
1052
1053```jsx
1054import {render, Transform} from 'ink';
1055
1056const Example = () => (
1057 <Transform transform={output => output.toUpperCase()}>
1058 <Text>Hello World</Text>
1059 </Transform>
1060);
1061
1062render(<Example />);
1063```
1064
1065Since `transform` function converts all characters to upper case, final output that's rendered to the terminal will be "HELLO WORLD", not "Hello World".
1066
1067#### transform(children)
1068
1069Type: `Function`
1070
1071Function which transforms children output.
1072It accepts children and must return transformed children too.
1073
1074##### children
1075
1076Type: `string`
1077
1078Output of child components.
1079
1080## Hooks
1081
1082### useInput(inputHandler, options?)
1083
1084This hook is used for handling user input.
1085It's a more convenient alternative to using `useStdin` and listening to `data` events.
1086The callback you pass to `useInput` is called for each character when user enters any input.
1087However, if user pastes text and it's more than one character, the callback will be called only once and the whole string will be passed as `input`.
1088You can find a full example of using `useInput` at [examples/use-input](examples/use-input/use-input.js).
1089
1090```jsx
1091import {useInput} from 'ink';
1092
1093const UserInput = () => {
1094 useInput((input, key) => {
1095 if (input === 'q') {
1096 // Exit program
1097 }
1098
1099 if (key.leftArrow) {
1100 // Left arrow key pressed
1101 }
1102 });
1103
1104 return …
1105};
1106```
1107
1108#### inputHandler(input, key)
1109
1110Type: `Function`
1111
1112The handler function that you pass to `useInput` receives two arguments:
1113
1114##### input
1115
1116Type: `string`
1117
1118The input that the program received.
1119
1120##### key
1121
1122Type: `object`
1123
1124Handy information about a key that was pressed.
1125
1126###### key.leftArrow
1127
1128###### key.rightArrow
1129
1130###### key.upArrow
1131
1132###### key.downArrow
1133
1134Type: `boolean`\
1135Default: `false`
1136
1137If an arrow key was pressed, the corresponding property will be `true`.
1138For example, if user presses left arrow key, `key.leftArrow` equals `true`.
1139
1140###### key.return
1141
1142Type: `boolean`\
1143Default: `false`
1144
1145Return (Enter) key was pressed.
1146
1147###### key.escape
1148
1149Type: `boolean`\
1150Default: `false`
1151
1152Escape key was pressed.
1153
1154###### key.ctrl
1155
1156Type: `boolean`\
1157Default: `false`
1158
1159Ctrl key was pressed.
1160
1161###### key.shift
1162
1163Type: `boolean`\
1164Default: `false`
1165
1166Shift key was pressed.
1167
1168###### key.tab
1169
1170Type: `boolean`\
1171Default: `false`
1172
1173Tab key was pressed.
1174
1175###### key.backspace
1176
1177Type: `boolean`\
1178Default: `false`
1179
1180Backspace key was pressed.
1181
1182###### key.delete
1183
1184Type: `boolean`\
1185Default: `false`
1186
1187Delete key was pressed.
1188
1189###### key.pageDown
1190
1191###### key.pageUp
1192
1193Type: `boolean`\
1194Default: `false`
1195
1196If Page Up or Page Down key was pressed, the corresponding property will be `true`.
1197For example, if user presses Page Down, `key.pageDown` equals `true`.
1198
1199###### key.meta
1200
1201Type: `boolean`\
1202Default: `false`
1203
1204[Meta key](https://en.wikipedia.org/wiki/Meta_key) was pressed.
1205
1206#### options
1207
1208Type: `object`
1209
1210##### isActive
1211
1212Type: `boolean`\
1213Default: `true`
1214
1215Enable or disable capturing of user input.
1216Useful when there are multiple `useInput` hooks used at once to avoid handling the same input several times.
1217
1218### useApp()
1219
1220`useApp` is a React hook, which exposes a method to manually exit the app (unmount).
1221
1222#### exit(error?)
1223
1224Type: `Function`
1225
1226Exit (unmount) the whole Ink app.
1227
1228##### error
1229
1230Type: `Error`
1231
1232Optional error. If passed, [`waitUntilExit`](waituntilexit) will reject with that error.
1233
1234```js
1235import {useApp} from 'ink';
1236
1237const Example = () => {
1238 const {exit} = useApp();
1239
1240 // Exit the app after 5 seconds
1241 useEffect(() => {
1242 setTimeout(() => {
1243 exit();
1244 }, 5000);
1245 }, []);
1246
1247 return …
1248};
1249```
1250
1251### useStdin()
1252
1253`useStdin` is a React hook, which exposes stdin stream.
1254
1255#### stdin
1256
1257Type: `stream.Readable`\
1258Default: `process.stdin`
1259
1260Stdin stream passed to `render()` in `options.stdin` or `process.stdin` by default.
1261Useful if your app needs to handle user input.
1262
1263```js
1264import {useStdin} from 'ink';
1265
1266const Example = () => {
1267 const {stdin} = useStdin();
1268
1269 return …
1270};
1271```
1272
1273#### isRawModeSupported
1274
1275Type: `boolean`
1276
1277A boolean flag determining if the current `stdin` supports `setRawMode`.
1278A component using `setRawMode` might want to use `isRawModeSupported` to nicely fall back in environments where raw mode is not supported.
1279
1280```jsx
1281import {useStdin} from 'ink';
1282
1283const Example = () => {
1284 const {isRawModeSupported} = useStdin();
1285
1286 return isRawModeSupported ? (
1287 <MyInputComponent />
1288 ) : (
1289 <MyComponentThatDoesntUseInput />
1290 );
1291};
1292```
1293
1294#### setRawMode(isRawModeEnabled)
1295
1296Type: `function`
1297
1298##### isRawModeEnabled
1299
1300Type: `boolean`
1301
1302See [`setRawMode`](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode).
1303Ink exposes this function to be able to handle <kbd>Ctrl</kbd>+<kbd>C</kbd>, that's why you should use Ink's `setRawMode` instead of `process.stdin.setRawMode`.
1304
1305**Warning:** This function will throw unless the current `stdin` supports `setRawMode`. Use [`isRawModeSupported`](#israwmodesupported) to detect `setRawMode` support.
1306
1307```js
1308import {useStdin} from 'ink';
1309
1310const Example = () => {
1311 const {setRawMode} = useStdin();
1312
1313 useEffect(() => {
1314 setRawMode(true);
1315
1316 return () => {
1317 setRawMode(false);
1318 };
1319 });
1320
1321 return …
1322};
1323```
1324
1325### useStdout()
1326
1327`useStdout` is a React hook, which exposes stdout stream, where Ink renders your app.
1328
1329#### stdout
1330
1331Type: `stream.Writable`\
1332Default: `process.stdout`
1333
1334```js
1335import {useStdout} from 'ink';
1336
1337const Example = () => {
1338 const {stdout} = useStdout;
1339
1340 return …
1341};
1342```
1343
1344#### write(data)
1345
1346Write any string to stdout, while preserving Ink's output.
1347It's useful when you want to display some external information outside of Ink's rendering and ensure there's no conflict between the two.
1348It's similar to `<Static>`, except it can't accept components, it only works with strings.
1349
1350##### data
1351
1352Type: `string`
1353
1354Data to write to stdout.
1355
1356```js
1357import {useStdout} from 'ink';
1358
1359const Example = () => {
1360 const {write} = useStdout();
1361
1362 useEffect(() => {
1363 // Write a single message to stdout, above Ink's output
1364 write('Hello from Ink to stdout\n');
1365 }, []);
1366
1367 return …
1368};
1369```
1370
1371See additional usage example in [examples/use-stdout](examples/use-stdout/use-stdout.js).
1372
1373### useStderr()
1374
1375`useStderr` is a React hook, which exposes stderr stream.
1376
1377#### stderr
1378
1379Type: `stream.Writable`\
1380Default: `process.stderr`
1381
1382Stderr stream.
1383
1384```js
1385import {useStderr} from 'ink';
1386
1387const Example = () => {
1388 const {stderr} = useStderr();
1389
1390 return …
1391};
1392```
1393
1394#### write(data)
1395
1396Write any string to stderr, while preserving Ink's output.
1397
1398It's useful when you want to display some external information outside of Ink's rendering and ensure there's no conflict between the two.
1399It's similar to `<Static>`, except it can't accept components, it only works with strings.
1400
1401##### data
1402
1403Type: `string`
1404
1405Data to write to stderr.
1406
1407```js
1408import {useStderr} from 'ink';
1409
1410const Example = () => {
1411 const {write} = useStderr();
1412
1413 useEffect(() => {
1414 // Write a single message to stderr, above Ink's output
1415 write('Hello from Ink to stderr\n');
1416 }, []);
1417
1418 return …
1419};
1420```
1421
1422### useFocus(options?)
1423
1424Component that uses `useFocus` hook becomes "focusable" to Ink, so when user presses <kbd>Tab</kbd>, Ink will switch focus to this component.
1425If there are multiple components that execute `useFocus` hook, focus will be given to them in the order that these components are rendered in.
1426This hook returns an object with `isFocused` boolean property, which determines if this component is focused or not.
1427
1428#### options
1429
1430##### autoFocus
1431
1432Type: `boolean`\
1433Default: `false`
1434
1435Auto focus this component, if there's no active (focused) component right now.
1436
1437##### isActive
1438
1439Type: `boolean`\
1440Default: `true`
1441
1442Enable or disable this component's focus, while still maintaining its position in the list of focusable components.
1443This is useful for inputs that are temporarily disabled.
1444
1445##### id
1446
1447Type: `string`\
1448Required: `false`
1449
1450Set a component's focus ID, which can be used to programmatically focus the component. This is useful for large interfaces with many focusable elements, to avoid having to cycle through all of them.
1451
1452```jsx
1453import {render, useFocus, Text} from 'ink';
1454
1455const Example = () => {
1456 const {isFocused} = useFocus();
1457
1458 return <Text>{isFocused ? 'I am focused' : 'I am not focused'}</Text>;
1459};
1460
1461render(<Example />);
1462```
1463
1464See example in [examples/use-focus](examples/use-focus/use-focus.js) and [examples/use-focus-with-id](examples/use-focus/use-focus-with-id.js).
1465
1466### useFocusManager()
1467
1468This hook exposes methods to enable or disable focus management for all components or manually switch focus to next or previous components.
1469
1470#### enableFocus()
1471
1472Enable focus management for all components.
1473
1474**Note:** You don't need to call this method manually, unless you've disabled focus management. Focus management is enabled by default.
1475
1476```js
1477import {useFocusManager} from 'ink';
1478
1479const Example = () => {
1480 const {enableFocus} = useFocusManager();
1481
1482 useEffect(() => {
1483 enableFocus();
1484 }, []);
1485
1486 return …
1487};
1488```
1489
1490#### disableFocus()
1491
1492Disable focus management for all components.
1493Currently active component (if there's one) will lose its focus.
1494
1495```js
1496import {useFocusManager} from 'ink';
1497
1498const Example = () => {
1499 const {disableFocus} = useFocusManager();
1500
1501 useEffect(() => {
1502 disableFocus();
1503 }, []);
1504
1505 return …
1506};
1507```
1508
1509#### focusNext()
1510
1511Switch focus to the next focusable component.
1512If there's no active component right now, focus will be given to the first focusable component.
1513If active component is the last in the list of focusable components, focus will be switched to the first component.
1514
1515**Note:** Ink calls this method when user presses <kbd>Tab</kbd>.
1516
1517```js
1518import {useFocusManager} from 'ink';
1519
1520const Example = () => {
1521 const {focusNext} = useFocusManager();
1522
1523 useEffect(() => {
1524 focusNext();
1525 }, []);
1526
1527 return …
1528};
1529```
1530
1531#### focusPrevious()
1532
1533Switch focus to the previous focusable component.
1534If there's no active component right now, focus will be given to the first focusable component.
1535If active component is the first in the list of focusable components, focus will be switched to the last component.
1536
1537**Note:** Ink calls this method when user presses <kbd>Shift</kbd>+<kbd>Tab</kbd>.
1538
1539```js
1540import {useFocusManager} from 'ink';
1541
1542const Example = () => {
1543 const {focusPrevious} = useFocusManager();
1544
1545 useEffect(() => {
1546 focusPrevious();
1547 }, []);
1548
1549 return …
1550};
1551```
1552
1553#### focus(id)
1554
1555##### id
1556
1557Type: `string`
1558
1559Switch focus to the component with the given [`id`](#id).
1560If there's no component with that ID, focus will be given to the next focusable component.
1561
1562```js
1563import {useFocusManager, useInput} from 'ink';
1564
1565const Example = () => {
1566 const {focus} = useFocusManager();
1567
1568 useInput(input => {
1569 if (input === 's') {
1570 // Focus the component with focus ID 'someId'
1571 focus('someId');
1572 }
1573 });
1574
1575 return …
1576};
1577```
1578
1579## API
1580
1581#### render(tree, options?)
1582
1583Returns: [`Instance`](#instance)
1584
1585Mount a component and render the output.
1586
1587##### tree
1588
1589Type: `ReactElement`
1590
1591##### options
1592
1593Type: `object`
1594
1595###### stdout
1596
1597Type: `stream.Writable`\
1598Default: `process.stdout`
1599
1600Output stream where app will be rendered.
1601
1602###### stdin
1603
1604Type: `stream.Readable`\
1605Default: `process.stdin`
1606
1607Input stream where app will listen for input.
1608
1609###### exitOnCtrlC
1610
1611Type: `boolean`\
1612Default: `true`
1613
1614Configure whether Ink should listen to Ctrl+C keyboard input and exit the app.
1615This is needed in case `process.stdin` is in [raw mode](https://nodejs.org/api/tty.html#tty_readstream_setrawmode_mode), because then Ctrl+C is ignored by default and process is expected to handle it manually.
1616
1617###### patchConsole
1618
1619Type: `boolean`\
1620Default: `true`
1621
1622Patch console methods to ensure console output doesn't mix with Ink output.
1623When any of `console.*` methods are called (like `console.log()`), Ink intercepts their output, clears main output, renders output from the console method and then rerenders main output again.
1624That way both are visible and are not overlapping each other.
1625
1626This functionality is powered by [patch-console](https://github.com/vadimdemedes/patch-console), so if you need to disable Ink's interception of output but want to build something custom, you can use it.
1627
1628###### debug
1629
1630Type: `boolean`\
1631Default: `false`
1632
1633If `true`, each update will be rendered as a separate output, without replacing the previous one.
1634
1635#### Instance
1636
1637This is the object that `render()` returns.
1638
1639##### rerender(tree)
1640
1641Replace previous root node with a new one or update props of the current root node.
1642
1643###### tree
1644
1645Type: `ReactElement`
1646
1647```jsx
1648// Update props of the root node
1649const {rerender} = render(<Counter count={1} />);
1650rerender(<Counter count={2} />);
1651
1652// Replace root node
1653const {rerender} = render(<OldCounter />);
1654rerender(<NewCounter />);
1655```
1656
1657##### unmount()
1658
1659Manually unmount the whole Ink app.
1660
1661```jsx
1662const {unmount} = render(<MyApp />);
1663unmount();
1664```
1665
1666##### waitUntilExit()
1667
1668Returns a promise, which resolves when app is unmounted.
1669
1670```jsx
1671const {unmount, waitUntilExit} = render(<MyApp />);
1672
1673setTimeout(unmount, 1000);
1674
1675await waitUntilExit(); // resolves after `unmount()` is called
1676```
1677
1678##### clear()
1679
1680Clear output.
1681
1682```jsx
1683const {clear} = render(<MyApp />);
1684clear();
1685```
1686
1687#### measureElement(ref)
1688
1689Measure the dimensions of a particular `<Box>` element.
1690It returns an object with `width` and `height` properties.
1691This function is useful when your component needs to know the amount of available space it has. You could use it when you need to change the layout based on the length of its content.
1692
1693**Note:** `measureElement()` returns correct results only after the initial render, when layout has been calculated. Until then, `width` and `height` equal to zero. It's recommended to call `measureElement()` in a `useEffect` hook, which fires after the component has rendered.
1694
1695##### ref
1696
1697Type: `MutableRef`
1698
1699A reference to a `<Box>` element captured with a `ref` property.
1700See [Refs](https://reactjs.org/docs/refs-and-the-dom.html) for more information on how to capture references.
1701
1702```jsx
1703import {render, measureElement, Box, Text} from 'ink';
1704
1705const Example = () => {
1706 const ref = useRef();
1707
1708 useEffect(() => {
1709 const {width, height} = measureElement(ref.current);
1710 // width = 100, height = 1
1711 }, []);
1712
1713 return (
1714 <Box width={100}>
1715 <Box ref={ref}>
1716 <Text>This box will stretch to 100 width</Text>
1717 </Box>
1718 </Box>
1719 );
1720};
1721
1722render(<Example />);
1723```
1724
1725## Testing
1726
1727Ink components are simple to test with [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library).
1728Here's a simple example that checks how component is rendered:
1729
1730```jsx
1731import React from 'react';
1732import {Text} from 'ink';
1733import {render} from 'ink-testing-library';
1734
1735const Test = () => <Text>Hello World</Text>;
1736const {lastFrame} = render(<Test />);
1737
1738lastFrame() === 'Hello World'; //=> true
1739```
1740
1741Check out [ink-testing-library](https://github.com/vadimdemedes/ink-testing-library) for more examples and full documentation.
1742
1743## Using React Devtools
1744
1745![](media/devtools.jpg)
1746
1747Ink supports [React Devtools](https://github.com/facebook/react/tree/master/packages/react-devtools) out-of-the-box.
1748To enable integration with React Devtools in your Ink-based CLI, run it with `DEV=true` environment variable:
1749
1750```
1751$ DEV=true my-cli
1752```
1753
1754Then, start React Devtools itself:
1755
1756```
1757$ npx react-devtools
1758```
1759
1760After it starts up, you should see the component tree of your CLI.
1761You can even inspect and change the props of components, and see the results immediatelly in the CLI, without restarting it.
1762
1763**Note**: You must manually quit your CLI via <kbd>Ctrl</kbd>+<kbd>C</kbd> after you're done testing.
1764
1765## Useful Components
1766
1767- [ink-text-input](https://github.com/vadimdemedes/ink-text-input) - Text input.
1768- [ink-spinner](https://github.com/vadimdemedes/ink-spinner) - Spinner.
1769- [ink-select-input](https://github.com/vadimdemedes/ink-select-input) - Select (dropdown) input.
1770- [ink-link](https://github.com/sindresorhus/ink-link) - Link component.
1771- [ink-gradient](https://github.com/sindresorhus/ink-gradient) - Gradient color component.
1772- [ink-big-text](https://github.com/sindresorhus/ink-big-text) - Awesome text component.
1773- [ink-image](https://github.com/kevva/ink-image) - Display images inside the terminal.
1774- [ink-tab](https://github.com/jdeniau/ink-tab) - Tab component.
1775- [ink-color-pipe](https://github.com/LitoMore/ink-color-pipe) - Create color text with simpler style strings in Ink.
1776- [ink-multi-select](https://github.com/karaggeorge/ink-multi-select) - Select one or more values from a list
1777- [ink-divider](https://github.com/JureSotosek/ink-divider) - A divider component.
1778- [ink-progress-bar](https://github.com/brigand/ink-progress-bar) - Configurable component for rendering progress bars.
1779- [ink-table](https://github.com/maticzav/ink-table) - Table component.
1780- [ink-ascii](https://github.com/hexrcs/ink-ascii) - Awesome text component with more font choices, based on Figlet.
1781- [ink-markdown](https://github.com/cameronhunter/ink-markdown) - Render syntax highlighted Markdown.
1782- [ink-quicksearch-input](https://github.com/Eximchain/ink-quicksearch-input) - Select component with fast quicksearch-like navigation.
1783- [ink-confirm-input](https://github.com/kevva/ink-confirm-input) - Yes/No confirmation input.
1784- [ink-syntax-highlight](https://github.com/vsashyn/ink-syntax-highlight) - Code syntax highlighting.
1785- [ink-form](https://github.com/lukasbach/ink-form) - Form component.
1786- [ink-task-list](https://github.com/privatenumber/ink-task-list) - Task list component.
1787
1788## Useful Hooks
1789
1790- [ink-use-stdout-dimensions](https://github.com/cameronhunter/ink-monorepo/tree/master/packages/ink-use-stdout-dimensions) - Subscribe to stdout dimensions.
1791
1792## Examples
1793
1794- [Jest](examples/jest/jest.js) - Implementation of basic Jest UI [(live demo)](https://ink-jest-demo.vadimdemedes.repl.run/).
1795- [Counter](examples/counter/counter.js) - Simple counter that increments every 100ms [(live demo)](https://ink-counter-demo.vadimdemedes.repl.run/).
1796- [Form with Validation](https://github.com/final-form/rff-cli-example) - Manage form state using [Final Form](https://github.com/final-form/final-form#-final-form).
1797- [Borders](examples/borders/borders.js) - Add borders to `<Box>` component.
1798- [Suspense](examples/suspense/suspense.js) - Use React Suspense.
1799- [Table](examples/table/table.js) - Render a table with multiple columns and rows.
1800- [Focus Management](examples/use-focus/use-focus.js) - Use `useFocus` hook to manage focus between components.
1801- [User Input](examples/use-input/use-input.js) - Listen to user input.
1802- [Write to stdout](examples/use-stdout/use-stdout.js) - Write to stdout bypassing main Ink output.
1803- [Write to stderr](examples/use-stderr/use-stderr.js) - Write to stderr bypassing main Ink output.
1804- [Static](examples/static/static.js) - Use `<Static>` to render permanent output.
1805- [Child process](examples/subprocess-output) - Render output from a child process.
1806
1807## Maintainers
1808
1809- [Vadim Demedes](https://github.com/vadimdemedes)
1810- [Sindre Sorhus](https://github.com/sindresorhus)