Creating a Highlighted Input Component with React and TypeScript
In this article, we’ll walk through the creation of a HighlightedInput
component using React
, TypeScript
, and various other tools. This component is designed to highlight dynamic text within an input field, such as environment variables, URLs, or placeholders wrapped in curly braces {{}}
. We'll also utilize the @faker-js/faker
library to generate mock data for testing.
Links
Youtube Video
Live Demo
Github Repo
Packages Used
Before we dive into the code, let's discuss some of the key packages involved:
-
React
: AJavaScript
library for building user interfaces React. -
TypeScript
: Superset ofJavaScript
providing type safety and enhanced tooling forReact
applications TypeScript. -
@faker-js/faker
: A powerful library for generating fake data, useful in development and testing. We will use it to create mock URL options Faker JS. -
Lucide-react
: A collection of icons as React components Lucide React. -
Tailwind CSS
: A utility-firstCSS
framework to style the component efficiently Tailwind CSS. -
shadcn-ui
: A collection of UI components for React applications Shadcn UI.
Component Overview
The HighlightedInput
component highlights text based on user input. For example, if the user types {{TEST_URL}}
, the component matches and highlights the TEST_URL
environment variable. You can define these variables in the options prop.
Here’s how we structure the component:
import { PropsWithChildren, useId } from "react";
import { InfoIcon } from "lucide-react";
import {
HoverCard,
HoverCardContent,
HoverCardTrigger,
} from "@/components/ui/hover-card";
import { Label } from "@/components/ui/label";
import { cn } from "@/lib/utils";
import CopyText from "./copy-text";
We import essential utilities like useId (for generating unique IDs), UI components (HoverCard, Label), and a helper function cn (to manage conditional class names).
Additionally, we define a regex pattern, REGEX = /({{.*?}})/g;
, to match text between {{
and }}
, allowing us to detect the variable-like placeholders in the input field.
Key Features
1. Handling the Input Field
The input field is where the user types, and its value
is managed via props like value
, onChange
, and onBlur
.
<input
className="w-full px-3 py-2 border rounded-lg focus:outline-none focus:border-blue-500"
id={id}
onBlur={onBlur}
onChange={onChange}
placeholder="Enter URL or paste text"
value={value}
/>
The value
is processed to identify and highlight the dynamic portions of the text (i.e., anything inside {{}}
).
2. Highlighting Dynamic Text
The component highlights parts of the input matching the regex by splitting the value
string:
value.split(REGEX).map((word, i) => {
if (word.match(REGEX) !== null) {
const foundOption = getOption(word, options);
return (
<HoverCard key={i}>
<HoverCardTrigger asChild>
<span className={foundOption ? "text-orange-500" : "text-red-500"}>
{word}
</span>
</HoverCardTrigger>
<HoverCardContent className={cn(foundOption ? "bg-white" : "bg-red-50")}>
{foundOption ? (
// Render option details
) : (
<div className="flex flex-col gap-y-2">
<InfoIcon className="w-6 h-6 text-red-600" />
<span className="text-base font-bold">Unresolved Variable</span>
<p className="text-sm">The variable {word} is not defined.</p>
</div>
)}
</HoverCardContent>
</HoverCard>
);
}
return <span key={i}>{word}</span>;
});
Each matched variable is displayed inside a HoverCard, providing additional details when hovered. If the variable is not found in the options, an error message is displayed.
3. Managing Options
The component accepts a list of options
through the options
prop. Each option consists of a name and value (e.g., TEST_URL
maps to https://example.com
). These options
are used to replace the matched dynamic text.
We use @faker-js/faker to generate mock data for the options:
const handleAddOption = () => {
setOptions((prev) => [
...prev,
{
name: faker.finance.accountName().replace(/\s/g, "_").toUpperCase(),
value: faker.internet.url(),
},
]);
};
By clicking a button, a new option is generated dynamically, providing a randomized environment variable name and URL.
Styles
We used Tailwind CSS to style the component, ensuring a clean and responsive UI. The following classes are applied:
.input-container {
@apply h-10 relative;
}
.input-container input {
@apply w-full h-full absolute inset-0 bg-transparent outline-none text-base z-10 px-2 rounded border-input;
}
.input-container .input-renderer {
@apply absolute inset-0 mx-2 text-base text-foreground flex items-center whitespace-nowrap overflow-x-auto select-none;
}
This ensures the input field has a consistent size, responsive styling, and custom behavior such as hiding the placeholder text when not empty.
Using the Component
Here’s how you can use the HighlightedInput
component within an application:
import { faker } from "@faker-js/faker";
import { useState } from "react";
import { Star, Trash2 } from "lucide-react";
import HighlightedInput, { getOptionValue } from "@/components/highlight-input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import CopyText from "@/components/copy-text";
function HighlightExample() {
const [options, setOptions] = useState([
{
value: "https://jsonplaceholder.typicode.com/todos",
name: "TEST_URL",
},
]);
const isUrl = (url: string): boolean => {
try {
new URL(getOptionValue(url, options));
// new URL(url);
return true;
} catch {
return false;
}
};
const optionNameFormat = (name: string) =>
name.replace(/\s/g, "_").toLocaleUpperCase();
const handleAddOption = () => {
setOptions((prev) => [
...prev,
{
name: optionNameFormat(faker.finance.accountName()),
value: faker.internet.url(),
},
]);
};
const handleDeleteOption = (index: number) => {
setOptions((prev) => prev.filter((_, i) => i !== index));
};
const [url, setUrl] = useState("https://jsonplaceholder.typicode.com/todos");
const resultUrl = isUrl(url) ? new URL(getOptionValue(url, options)) : "";
const finalUrl = typeof resultUrl === "object" ? resultUrl.href : "";
return (
<div className="w-full">
<div className="flex flex-row gap-4">
<HighlightedInput
onChange={(event) => setUrl(event.target.value)}
options={options}
value={url}
className="mb-4"
/>
<Button className="mb-4" onClick={handleAddOption}>
Add Option
</Button>
</div>
<article className="flex flex-row gap-x-2">
<Label className="text-lg select-none">Result URL:</Label>
<Label weight={"bold"} className="text-lg select-none">
{finalUrl}
</Label>
<CopyText text={finalUrl} />
</article>
<article className="py-4">
<Label className="text-lg">
Options:{" "}
<Label className="text-xs">
(Click on the trash icon to delete an option)
</Label>
</Label>
</article>
<section
hidden={options.length === 0}
className="overflow-hidden border rounded-lg"
>
{options.map((option, index) => (
<div
key={index}
className="flex flex-row items-center justify-center px-4 py-2 border-b gap-x-2 last:border-none hover:bg-primary-foreground hover:bg-opacity-10"
>
<Star
size={16}
className="text-primary fill-primary dark:text-muted-foreground dark:fill-muted-foreground"
/>
<Label
className="flex flex-row items-center select-none"
weight={"bold"}
>
<CopyText text={option.name} />
{option.name}:
</Label>
<Label className="select-none">{option.value}</Label>
<Button
variant={"ghost"}
className="p-2 m-0 ml-auto mr-0 cursor-pointer text-destructive"
>
<Trash2 size={16} onClick={() => handleDeleteOption(index)} />
</Button>
</div>
))}
</section>
</div>
);
}
export default HighlightExample;
In this example, we define a Usage
component that utilizes the HighlightedInput
component. We manage the options, handle URL validation, and display the result URL based on the input.
Conclusion
In this article, we built a HighlightedInput
component using React
, TypeScript
, and various other tools. This component allows users to highlight dynamic text within an input field, such as environment variables or URLs. We also explored key features like handling the input field, highlighting dynamic text, and managing options. By using @faker-js/faker
, we generated mock data for testing purposes. This component can be a valuable addition to your projects, enhancing user experience and providing a clean interface for managing dynamic text inputs.
I hope you found this article helpful. Feel free to reach out with any questions or feedback. Happy coding!
Top comments (0)