spot_img
HomeEducationUtilizing Ink UI with React to construct interactive, customized CLIs - LogRocket...

Utilizing Ink UI with React to construct interactive, customized CLIs – LogRocket Weblog | The Global Today

CLIs are improbable! With only one command out of your terminal, you possibly can execute a number of scripts to automate the vast majority of the repetitive procedures.

Nevertheless, the boring terminal fonts offered by most CLIs could make for a fairly boring consumer expertise. It will be much better should you might make your CLI extra customized and interactive.

The excellent news is that because of Ink, you possibly can create CLIs that leverage the ability of React! Bringing React’s capabilities to the world of CLIs lets you create an interactive UI with reusable parts.

This text will discover the ability of Ink and its accompanying assortment of UI parts, Ink UI, via a demo mission the place we’ll create a primary to-do CLI software. We are going to cowl:

You possibly can check with the project’s GitHub repo to comply with together with this tutorial

What’s Ink, and what’s Ink UI?

Ink is a framework that gives the identical consumer expertise seen in React purposes, however for a CLI. It makes use of the ability of React to construct component-based command-line purposes, making it an effective way to construct interactive command-line instruments.

Ink leverages Yoga, a format engine that helps construct Flexbox layouts into the applying. It additionally goes hand-in-hand with Ink UI, a library similar to Ant UI, Subsequent UI, and Chakra UI for React purposes that gives a wide range of helpful parts.

Utilizing Ink and Ink UI collectively, you possibly can make the most of parts comparable to textual content inputs, alerts, lists, and extra to customise your CLI.

Organising an Ink mission

One of the best ways to find out about any new expertise is to construct one thing with it. By the top of this tutorial, you’ll have the ability to construct an interactive to-do CLI utility utilizing Ink and Ink UI.

Let’s begin off by first putting in Ink. Fortuitously, Ink has offered create-ink-app as a straightforward means to take action. Go forward and set up Ink utilizing the command under:

npx create-ink-app --typescript todo-cli

Right here’s the file construction you possibly can count on to see after putting in:

The supply folder, which comprises the app.tsx file, is the one folder you’ll work with whereas creating.

You possibly can outline many flags or variables to make use of when typing the command within the terminal utilizing the cli.tsx file. As an illustration, construct the applying as soon as utilizing the npm run construct command after which kind the next within the terminal the place your mission is situated to make use of the CLI instantly:

todo-cli identify=Elon

You’ll be welcomed with a message studying {Hello} Elon. Defining this identify flag lets you use it to invoke any custom-made motion within the CLI software.

Putting in the Ink UI package deal

Subsequent up, let’s begin constructing our CLI software by putting in the Ink UI package deal:

npm set up @inkjs/ui

With that achieved, let’s simply see if every thing’s working as anticipated. In app.tsx, substitute the element getting returned with an Ink UI element:

import  Choose  from "@inkjs/ui";

kind Props = 
    identify: string ;

export default operate App(identify="Stranger": Props) 
  return (
    <Choose
      choices=[
      label: 'Help', value: 'help',
      label: 'Create Task', value: 'create-task',
      label: 'Delete Task', value: 'delete-task',
      label: 'Complete Task', value: 'complete-task',
      label: 'View tasks', value: 'view-task',
      label: 'Exit', value: 'exit',
     ]
    onChange=worth => 
      console.log(worth);
    
  />
  )

The code above establishes the fundamental format of the CLI software. The consumer will have the ability to create, delete, and full duties by interacting with the Choose element after we construct the software.

Let’s run the CLI to see what we now have thus far.

Word that as an alternative of operating npm run construct every time to construct the CLI software, we’ll run the npm run dev command. It will open up an occasion that constantly checks for adjustments within the codebase and rebuilds immediately with the most recent model of the CLI software.

Go forward and run the CLI:

Simple Ui Rendered After Running Default Ink Cli Showing Simple List Of To Do Options

As soon as that’s achieved, let’s see how we are able to create reusable parts by using React’s options.

Creating reusable parts utilizing Ink, Ink UI, and React

Create a brand new folder below supply and provides it the identify parts.

Then, create a brand new element referred to as ContainerElement.tsx. We’ll additionally use a number of components from the built-in Ink library right here:

import Field, Textual content from 'ink';
import BigText from 'ink-big-text';
import Gradient from 'ink-gradient';
import React from 'react';

operate ContainerElement(kids: kids: any) 
    return (
        <Field flexDirection="row" hole=5>
            <Field flexDirection="column" width='70%'>
                <Gradient identify="fruit">
                    <BigText textual content="To-Do Bro" />
                </Gradient>
                <Textual content>
                    Lorem ipsum, dolor sit amet consectetur adipisicing elit. In....
                </Textual content>
            </Field>
            <Field justifyContent="heart" flexDirection="column">
                kids
            </Field>
        </Field>
    );


export default ContainerElement;

The Field element is sort of helpful when creating the CLI utility format, as the identical Flexbox guidelines apply as in internet improvement.

In the meantime, we’re offering many choices for the consumer to navigate in our CLI software. We are able to reuse our newly created ContainerElement element to assist comprise and arrange all of them. Let’s begin utilizing it now by wrapping it across the Choose element we began with:

<ContainerElement>
  <Choose
    choices=[
      label: 'Help', value: 'help',
      label: 'Create Task', value: 'create-task',
      label: 'Delete Task', value: 'delete-task',
      label: 'Complete Task', value: 'complete-task',
      label: 'View tasks', value: 'view-task',
      label: 'Exit', value: 'exit',
    ]
    onChange=worth => 
      console.log(worth);
    
  />
</ContainerElement>

It’s best to now have the ability to see the next upon operating the CLI:

Custom Cli Shown After Applying New Custom Reusable Containerelement Component Showing New Styled To Do List Title, Lorem Ipsum Dummy Text, And List Of Options To Create, Delete, Complete, Or View Tasks Rendered At Right Side

Subsequent, we have to resolve precisely how we are going to use the CLI program to carry out the varied actions out there, comparable to including a brand new process, deleting an present process, and extra.

Fortuitously, React’s useState Hook may help us monitor the standing of the CLI utility at any given time. Each time the state adjustments, your complete utility is re-rendered to mirror these adjustments, giving customers a contemporary expertise.

Add the next to the Choose element:

<Choose
  choices=[
    label: 'Help', value: 'help',
    label: 'Create Task', value: 'create-task',
    label: 'Delete Task', value: 'delete-task',
    label: 'Complete Task', value: 'complete-task',
    label: 'View tasks', value: 'view-task',
    label: 'Exit', value: 'exit',
  ]
  onChange=worth => 
    setFeature(worth);
    if (worth === 'exit') course of.exit();
  
/>

Subsequent, we’ll use the useState Hook to set which of the choices above is at present chosen quite than logging the chosen worth to the console:

const [feature, setfeature] = useState("");

Now, every time the consumer selects an choice, the state is ready to that specific choice’s worth property, which is outlined contained in the choices array. If the consumer selects Exit, then we exit the present CLI occasion.

Organising the logic for creating and finishing duties

Let’s arrange the fundamental logic for making a process. Right here, we are able to make use of the filesystem fs module from Node.js to create native information to learn and write the duty onto it.

Word that if we had been working with a standard React utility, we wouldn’t have the ability to use the fs module — it solely works on the server facet, not on the shopper facet. However on this case, since we’re utilizing it contained in the terminal, it’s not a difficulty.

Contained in the app.tsx file, add an if...else situation like so:

if (function === "create-task") 
  // render Create process part
 else 
  // render the house display

Within the Create Activity part, we’ll merely render a TextInput element for the consumer to enter their process info:

import fs from "fs";

// contained in the App element
if (function === 'create-task') {
  return (
    <ContainerElement>
      <TextInput
        placeholder="Enter your process"
        onSubmit={textual content=> 
          fs.readFile('process.txt', (err, information) =>  information.size === 0) 
              fs.writeFile('process.txt', textual content, err => 
                if (err) throw err;
              );
            

            // append duties to present file
            fs.appendFile('process.txt', `n$textual content`, err => 
              if (err) throw err;
            );
          );

          // goes again to dwelling display
          setFeature('');
          }
      />
    </ContainerElement>
  );
}

Let’s overview what occurs when the consumer enters a brand new process:

  • First, fs.readFile() tries to test if there’s any file with the file identify process.txt
  • If there’s no such file out there, or if there is no such thing as a content material contained in the file, then the fs.writeFile() is invoked, which writes onto the file and creates the duty
  • If there’s a file current with content material in it, then fs.appendFile() will append the user-entered textual content onto the file. Word that we’re including the newline character n in order that the following process begins from a brand new line
  • Lastly, the function state is ready to an empty string, therefore switching over to the startup display of the CLI utility

Subsequent, let’s arrange the logic for finishing a process. We might merely take away that specific process from the present listing of duties. However to take action, we now have to fetch all the present duties first. We’ll make use of the fs module once more for this:

const [readData, setReadData] = useState([]);

// if-else statements
else if (function === 'complete-task') {
  fs.readFile('process.txt', 'utf-8', (err, information) => 
    if (err) throw err;
    let cut up = information.cut up('n');
    setReadData(cut up);
  );

// to be continued...

Because the information contained in the textual content file is separated by the newline character, we are able to use the cut up methodology to get all of the duties within the type of an array, which may be set as a state. Then, we’ll show these duties utilizing the Choose element:

<ContainerElement>
  <Choose
    choices=readData?.map((merchandise: any, index: quantity) => (
      label: merchandise,
      worth: index.toString(),
      ))
    onChange=worth => 
      let information = readData.filter((merchandise: any, index) => index !== +worth);
      if (information) 
        fs.writeFile('process.txt', information.be a part of('n'), err => 
          if (err) throw err;
        );
      setFeature('');
      
    
  />
</ContainerElement> 

The map methodology is used right here to cross the choices to the Choose element. Upon deciding on a specific process to mark as accomplished, the readData state is filtered via to return solely the weather excluding that process.

Lastly, utilizing the file-system module, the brand new listing of present duties is written onto the identical process.txt file, and the function state is ready to an empty string. With this achieved, you ought to be now in a position to full duties by eradicating them from the duty listing file.

Alternatively, we might additionally mark duties as full by shifting them to a separate file that completely comprises accomplished duties. Later, these accomplished duties can be fetched within the CLI to be displayed.

Right here’s how our customized CLI ought to look thus far:

Custom Cli With New Tasks Added

Displaying our process listing in our CLI

Let’s work on our view-task function. We wish to show these duties in an ordered listing and provides the consumer the choice to return to the primary menu, so we’ll be utilizing two Ink UI parts referred to as OrderedList and ConfirmInput.

We’ll begin by getting all the present duties from the process.txt file:

else if (function === 'view-task') 
  fs.readFile('process.txt', 'utf-8', (err, information) => 
    if (err) throw err;
    let cut up = information.cut up('n');
    setReadData(cut up);
  );

Subsequent, utilizing the OrderedList element, we’ll show our listing of duties in a numbered listing. They may present up within the order wherein we created them. Go forward and create a brand new file within the parts folder with the next code:

import OrderedList from '@inkjs/ui';
import Textual content from 'ink';
import React from 'react';

interface Props 
    gadgets: Array<string>;


operate DisplayItems(gadgets: Props) 
    return (
        <OrderedList>
            gadgets.map((merchandise, index) => (
                <OrderedList.Merchandise key=index>
                    <Textual content>merchandise</Textual content>
                </OrderedList.Merchandise>
            ))
        </OrderedList>
    );

export default DisplayItems;

This operate merely shows the duties so as utilizing Ink UI’s OrderedList element. We’ll return to the app.tsx file and import this element. Let’s additionally make certain the consumer has the choice to return to the primary web page with the ConfirmInput element:

return (
  <ContainerElement>
    <DisplayItems gadgets=readData />
    <Field marginTop=1 flexDirection="row" hole=2>
      <Textual content>Return?</Textual content>
      <ConfirmInput
        onCancel=() => setFeature('view-task')
        onConfirm=() => setFeature('')
      />
    </Field>
  </ContainerElement>
);

The Field and the Textual content parts include Ink, so make certain to import them within the file earlier than utilizing them. The Field element lets you create flex layouts similar to you’d in an internet utility.

The ConfirmInput element offers two callbacks — onCancel and onConfirm. When the consumer confirms, they are going to be redirected to the house display. We obtain this performance by setting the function state to an empty string. If the consumer cancels, they’ll keep on the identical web page.

Now, as you possibly can see, we ask the consumer in the event that they wish to Return? and take a easy “sure” or “no” Y/n enter from the consumer utilizing the ConfirmInput element. As soon as the consumer confirms “sure” utilizing Y, we’ll take them again to the primary web page:

Custom Cli Displaying Ordered Task List And Option To Return To Main Screen

To see your full utility, hit npm run construct — or should you’re in improvement mode, npm run dev. Ink routinely creates a brand new construct each time you make any change.

Congratulations on reaching the top of this tutorial! Now that you just’ve seen Ink and Ink UI in motion, you possibly can go forward and make your personal CLI utility seamlessly 🎉

Conclusion

Ink is turning into much more in style as a consequence of its low studying curve, which is made doable because of its React context. Many well-known tech giants like Gatsby, GitHub Copilot, Prisma, Shopify, and others use Ink to develop their CLI instruments.

With its declarative component-based method, Ink has emerged as a game-changer within the realm of CLI utility improvement. It leverages the ability of React to permit builders to harness their present information and expertise, making it an accessible alternative for React builders.

On this tutorial, we lined easy methods to use a number of of the parts and capabilities of Ink and Ink UI to create our demo mission. You possibly can try what else is out there to make use of in Ink UI’s GitHub documentation.

I’m excited to see what you’ll create with Ink! When you’ve got any questions, be happy to remark under.

  1. Go to to get an app ID.
  2. Set up LogRocket through NPM or script tag. LogRocket.init() should be referred to as client-side, not server-side.
  3. $ npm i --save logrocket 

    import LogRocket from 'logrocket';
    LogRocket.init('app/id');

    Add to your HTML:

    <script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
    <script>window.LogRocket && window.LogRocket.init('app/id');</script>

  4. (Optionally available) Set up plugins for deeper integrations along with your stack:
    • Redux middleware
    • ngrx middleware
    • Vuex plugin

Get started now

#Ink #React #construct #interactive #customized #CLIs #LogRocket #Weblog

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Skip to toolbar