A simple terminal emulator component for React
A simple, powerful and highly customisable terminal emulator for React.
react-console-emulator
A simple, powerful and highly customisable Unix terminal emulator for React.
Live demo
Features
- Highly customisable: Add custom responses, restyle and tweak the terminal to your liking and much more.
- A Unix terminal in the browser: Accurately emulate a native Unix terminal in the browser with no setup required.
- Familiar shortcuts: The terminal can keep track of commands and allows the user to recall them at their behest.
- Easy and powerful command system: Execute code from your own application and send the results to the terminal output.
- Async output support: Push output to the terminal at any time, even after a command response has been emitted.
- Unlimited concurrency: Register as many terminals as you like with no risk of input confusion.
Usage
import React from 'react'
import Terminal from 'react-console-emulator'
const commands = {
echo: {
description: 'Echo a passed string.',
usage: 'echo <string>',
fn: function () {
return `${Array.from(arguments).join(' ')}`
}
}
}
export default class MyTerminal extends React.Component {
render () {
return (
<Terminal
commands={commands}
welcomeMessage={'Welcome to the React terminal!'}
promptLabel={'[email protected]:~$'}
/>
)
}
}
Props
Prop | Description | Type |
---|---|---|
commands | Commands for the terminal. See Command syntax for more information. | Object |
commandCallback | Function to execute after each command. See commandCallback for more information. | Function |
welcomeMessage | Welcome message(s) to display in terminal. Set to true to enable the default welcome message, pass an array to send multiple separate messages, or omit to disable the welcome message entirely. |
String/Array/Boolean |
promptLabel | Custom prompt label displayed in front of the command input. Omit to enable the default label of $ . |
String |
errorText | Custom error text displayed when an unknown command is run. Omit to enable the default message. The placeholder [command] in the error string provides the command name that was input. |
String |
background | Terminal background. Accepts any background that CSS recognises. | String (Valid CSS) |
backgroundSize | The background-size CSS property for the terminal background. | String (Valid CSS) |
autoFocus | Automatically focus the terminal on page load. | Boolean |
dangerMode | Parse command responses as HTML. Warning: This may open your application to abuse. It is recommended that you employ anti-XSS methods to validate command responses when using this option. | Boolean |
disableOnProcess | Disable terminal input while a command is being processed. | Boolean |
noDefaults | Do not register default commands. Warning: If you enable this, you must manually create all commands or otherwise the terminal will be moot. | Boolean |
noAutomaticStdout | Disable all automatic output. | Boolean |
noHistory | Disable command history. | Boolean |
noAutoScroll | Disable the behaviour where the terminal scrolls to the bottom after each command. | Boolean |
textColor | The colour of the text in the terminal, minus the prompt label and input. | String (Valid CSS) |
promptLabelColor | The colour of the prompt label. | String (Valid CSS) |
promptTextColor | The colour of the text in the command input field. | String (Valid CSS) |
className | The CSS class name of the root element. | String |
contentClassName | The CSS class name of the terminal content container (Stdout + prompt + input). | String |
inputAreaClassName | The CSS class name of the input area (Prompt + input). | String |
promptLabelClassName | The CSS class name of the prompt label. | String |
inputClassName | The CSS class name of the input element. | String |
contentFontFamily | The font family to use for the terminal content container (Stdout + prompt + input) | String (Valid CSS) |
inputFontFamily | The font family to use for the input element. | String (Valid CSS) |
Method reference
commandCallback
If passed as a prop, the commandCallback function is executed each time a command completes execution. Returns an object with information about the command that was executed and the result thereof.
Command result object reference
Property | Description | Type |
---|---|---|
command | The command that was executed. | String |
args | An array of the arguments that were passed to the command. | Array |
rawInput | A string with the unprocessed input that was entered in the terminal. | String |
result | The return value of the function executed in the command. | Any |
Example:
commandCallback={result => console.log(result)}
/*
{
command: 'help',
args: [ 'test', 'test2' ]
rawInput: 'help test test2',
result: 'This is the help command.'
}
*/
Command syntax
Commands are passed to the component in the following format.
Each command must have a fn
property. All other properties are optional.
const commands = {
commandName: {
description: 'Optional description',
usage: 'Optional usage instruction',
fn: function (arg1, arg2) { // You may also use arrow functions
// Arguments passed to the command will be passed to this function in the same order as they appeared in the terminal
// You can execute custom code here
const lowerCaseArg1 = arg1.toLowerCase()
// What you return in this function will be output to the terminal
return `test ${lowerCaseArg1}`
},
explicitExec: true, // If you need to execute your function again after the output has been emitted, enable
}
}
Async output
If you terminal deals with HTTP requests or cross-component functionality, you may need to wait for a result before pushing to the output without relying on the function return time.
Note: Doing output this way is a workaround, and ideally your output should be returned by the command function. This method will expose functions to you that you do not normally have access to due to React component encapsulation. Proceed with caution.
To do this, you can use the React refs API. Below is an example component that uses async pushing.
import React from 'react'
import Terminal from 'react-console-emulator'
class MyTerminal extends React.Component {
constructor (props) {
super(props)
this.terminal = React.createRef()
}
// Experimental syntax, requires Babel with the transform-class-properties plugin
// You may also define commands within render in case you don't have access to class field syntax
commands = {
wait: {
description: 'Waits one second and sends a message.'
fn: () => {
const terminal = this.terminal.current
setTimeout(() => terminal.pushToStdout('Hello after 1 second!'), 1500)
return 'Running, please wait...'
}
}
}
render () {
return (
<Terminal
ref={this.terminal} // Assign ref to the terminal here
commands={commands}
/>
)
}
}
The function of the wait
command hooks into the terminal lifecycle (See Terminal lifecycle for more on that) and pushes content to the output of the terminal after the command function has already terminated. This way, you can perform tasks elsewhere and push the output to the terminal when you get the result.
The only notable caveat of this method is the breaking of component encapsulation. This is the trade-off for being able to push content on demand.
Note: Assigning a ref to the terminal component exposes all of its functions and properties. As such, you should take adequate measures against this being abused by end users and treat an exposed terminal with caution in general.
Terminal lifecycle
Per standard, the terminal operates in the following way when a command is entered. You can hook into these processes when the terminal is exposed via the refs API.
- A key event triggers the handleInput function.
- The handleInput function behaves as follows:
- If the either up or down arrow was pressed, scrollHistory is called with either
up
ordown
as a parameter, corresponding to the arrow key that was pressed. - If the Enter key was pressed, processCommand is called.
- If the either up or down arrow was pressed, scrollHistory is called with either
- Following the Enter path, if automatic output isn't disabled via the
noAutomaticStdout
prop, pushToStdout is called for the first time. This echoes the command that was entered into the terminal verbatim to mimic a UNIX terminal.- If history isn't disabled via the
noHistory
prop, the entered command is also stored in the history at this stage.
- If history isn't disabled via the
- If the input isn't empty, command processing begins.
- If the command doesn't exist, an error message is pushed to the output. If a custom error text is set via the
errorText
prop, it takes precedence over the default one. - If the command exists, the command function is executed and the return value of that function is pushed to the terminal (Without storing the return value in history). If the
explicitExec
property on the command object is truthy, the function will explicitly execute a second time after the output being sent.
- If the command doesn't exist, an error message is pushed to the output. If a custom error text is set via the
- The clearInput function is called.
- If automatic scrolling isn't disabled via the
noAutoScroll
prop, the terminal will scroll to the bottom of the output. - If a command callback function is defined via the
commandCallback
prop, it is called at this stage.
License
MIT © Linus Willner and Curtis Fowler.
"React" and any associated logos are trademarks of Facebook, Inc.