Yeah, you heard me right, the react js that you love so much, now you can use it to build CLI tools. This means all those nice features that react supports, you can use them in your CLI tool, conditional rendering, hooks, state management, and many more.
In this blog, we will learn about how we can use react to build CLI application with the help of inkjs
Setup the Project
mkdir react-ink-cli && cd react-ink-cli
npm init -y
npm install react ink ink-router-cli ink-spinner axios
npm install typescript -D
as we are using typescript we need to set up the tsconfig.json
file
{
"compilerOptions": {
"target": "es2015",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "src",
"strict": true,
"jsx": "react",
"declaration": true,
"strictNullChecks": true,
"sourceMap": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"esModuleInterop": true,
"skipLibCheck": true,
"experimentalDecorators": true
},
"include": ["src"],
"exclude": ["node_modules", "**/*.spec.ts"]
}
now inside the package.json
"scripts": {
"build": "tsc"
}
What are we going to build?
We are going to build a simple CLI program that will search a given GitHub username and fetch all the details about that username.
Now create a src/index.tsx
file, this is where our program will start
import React from 'react';
import { render } from 'ink';
import App from './app';
render(<App />)
Now create an src/app.tsx file
, this is where we will handle all the parser logic.
import React from 'react';
import { Router, Switch, Command } from 'ink-router-cli';
import User from './components/user';
const App = () => {
return <Router argv={process.argv} name="gh" >
<Switch>
<Command name="user" component={<User />} />
</Switch>
</Router>
}
module.exports = App;
export default App;
Now create an src/components/user.tsx
file, this is where we are going to write all the logic for the command
import React, { useState, useEffect } from 'react';
import { Box, Text, Newline } from 'ink';
import Spinner from 'ink-spinner';
import Axios from 'axios';
import { useCli } from 'ink-router-cli';
const User = () => {
let userName = useCli().arguments[0];
if (!userName) {
return <Text color="red">Please enter a valid username</Text>
}
const [isLoading, setLoading] = useState(false);
const [user, setUser] = useState() as any;
const [error, setError] = useState() as any;
const fetchUserData = async () => {
setLoading(true)
try {
let res = await Axios({
url: `https://api.github.com/users/${userName}`,
method: 'GET'
})
setUser(res.data);
setLoading(false);
} catch (error) {
setError("Could'nt find this username");
setLoading(false);
}
}
useEffect(() => {
fetchUserData();
}, [])
if (isLoading) {
return <Box paddingX={2} paddingY={1}>
<Text>
<Text color="green">
<Spinner type="dots" />
</Text>
{` Loading`}
</Text>
</Box>
}
if (user) {
return <Box paddingX={2} paddingY={1}>
<Box flexDirection="row" justifyContent="center" borderStyle="round" borderColor="green" paddingX={1}>
<Text>
{Object.keys(user).map(key => <Text key={key}>
<Text> <Text color="magenta">{key}</Text> : <Text color="cyan">{user[key]}</Text> </Text>
<Newline />
</Text>)}
</Text>
</Box>
</Box>
}
if (error) {
return <Text color="red">{error}</Text>
}
return <></>
}
export default User;
module.exports = User;
You are writing legit react code for building a CLI tool. As you can see you can do everything that you would do in react.
You can find the code here