Interactive E-Com UI Tutorial: Typing Searchbar
Tags: webdev, react, ui, animation
Published: 2023-06-12 13:10:37

## Introduction
In this article, the second in our Interactive E-commerce UI series, we aim to create a dynamic typing search bar using React. We will once again implement this into our existing Shopify Hydrogen store.
This search bar, intended for use in the header of an e-commerce website, will display and animate a series of words, simulating a typing effect.

As an e-commerce store owner implementing this, the main aim for our typing search bar is to inspire users and nudge them towards searching for something they might be looking for.


![Typing Searchbar](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/je7axdmmxjwvel3lw0f4.gif)

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tfkqjv6slev1qwtnxvue.gif)



##Setting the Stage
In the current era of digital commerce, user engagement and interaction are key factors in building a successful online store. One way to increase user engagement is through the use of animations and interactive elements. For every implementation of a new feature or animation, we should aim to change user behavior. Once way to do this is by utilizing human behavior patterns.
For our typing search bar, we're capitalizing on the familiar behavioral pattern of curiosity:

Curiosity
People are curious. They have a desire to experience the unknown and, in particular, to get to know what is hidden. Curiosity leads to trying out new things and thus initiating change.

Just as in the [last article](https://dev.to/codesphere/interactive-e-com-ui-animated-free-shipping-bar-156o), this pattern will be the basis of our design. In a real world scenario where we'd test out this feature to see if an uplift in conversion rate can be achieved, we would use those to set up a hypothesis.

## Implementation

We'll be implementing this search bar using React and the Remix framework, both come out of the box with Shopify Hydrogen. Remix provides a productive, integrated development experience and is perfect for building interactive UI components like our typing search bar.
This could also be implemented via vanilla JavaScript and HTML. If you're interested in this, please leave a comment and we'll make sure to create a guide on that too!

**Creating the TypingSearchBar Component**
Now, with the stage set, let's create a new component for our typing search bar. In this component, we'll use React's state and effect hooks to manage and animate the typing effect.

**Import Dependencies and Components**
First, import the necessary libraries and components. We'll use Form from Remix for the search functionality and useState and useEffect from React to manage the state and animation logic. In this case, we use the Form component, since we're manipulating the existing search in our Shopify Hydrogen theme. For your project, this might not be necessary. We also import the Input and IconSearch components for the search bar and search icon, which are also part of the Shopify Hydrogen theme.

```tsx
import { Form } from '@remix-run/react';
import React, { useState, useEffect } from 'react';
import { Input, IconSearch } from '~/components';
```

**Define the TypingSearchBar Component**
Next, define the TypingSearchBar component. It receives three props: `isHome`, `params`, and `variant`.These props are once again defined by our Shopify Hydrogen theme and are responsible for context specific styling and translation. In your example you might not need them at all or completely different ones.
We're going to continue with them in our code for this article.

```tsx
export function TypingSearchBar({
  isHome, // Boolean flag indicating if the component is being used on the home page
  params, // Object containing parameters (e.g., locale)
  variant // Determines the variant of the search bar ('search' or 'minisearch')
}: {
  isHome: boolean;
  params: { locale?: string };
  variant: 'search' | 'minisearch';
}) { ... } // Next Snippet -> Managing the State
```

**Manage the State**
Inside the component, we declare several state variables for managing the animation.

```tsx
// Array of words to display and animate
const words = ['Inspire', 'Search'];

// State variables for managing animation
const [index, setIndex] = useState(0); // Current index of the word array
const [subIndex, setSubIndex] = useState(0); // Current index within the selected word
const [word, setWord] = useState(''); // Current word being built
const [isVisible, setIsVisible] = useState(true); // Flag indicating if the word is visiblequote("Creating the TypingSearchBar Component\n\nWith", "visible; // Flag indicating if the word is visible")

// Timing durations for animation
const displayDuration = 3000; // Duration to display a fully typed word
const idleDuration = 250; // Duration before transitioning to the next word
```

**Implement the Animation**
Since we're replacing the placeholder text of a form, the implementation of the animation was a lot more tricky than initially expected. For a simple typing animation, you could use a library like [typewriter-effect](https://www.npmjs.com/package/typewriter-effect).

This animation, at its heart, is orchestrated by a `useEffect` hook. The `useEffect` hook in React is a function that lets you perform side effects in function components. Here, we use it to manage the sequencing and timing of the typing animation.

Inside the `useEffect` hook, we've got a few conditionals to manage the different stages of the animation. Let's walk through them:

- **Typing out the word**: If `subIndex` is less than the length of the current word (`words[index].length`), it means we are still in the process of typing out the word. We then set a timeout of 150 milliseconds before adding the next character to the word and incrementing subIndex by 1. This gives us the effect of each character being typed out one after the other with a slight delay, like a person typing.

- **Waiting once the word is fully typed**: If the `subIndex` is not less than the length of the word, and the `isVisible` flag is true, it means the word has been fully typed out and is currently visible. We then set a timeout of 3000 milliseconds (3 seconds) before setting the `isVisible` flag to false. This gives us a pause where the fully typed word is visible for a bit before it starts to disappear.

- **Deleting the word**: If the `isVisible` flag is false and `word` still has characters in it, it means we are in the process of "deleting" the word. We set a timeout of 100 milliseconds before removing the last character from `word`. This gives us the effect of the word being deleted one character at a time.

- **Transitioning to the next word**: If the `isVisible` flag is false and the `word` is empty, it means we've deleted the entire word and are ready to move on to the next word. We set a timeout of 250 milliseconds before resetting `word`, `isVisible`, and `subIndex` to their initial states, and incrementing index by 1 (or resetting it to 0 if it's at the end of the `words` array). This gives us a brief pause before the next word starts being typed out.

The `useEffect` hook is set to run every time `subIndex`, `index`, `word`, or `isVisible` changes. This means every time we change these variables (which we do inside the `useEffect` hook), the `useEffect` hook will run again, creating a continuous loop of typing, waiting, deleting, waiting, and so on.

This carefully choreographed sequence of timeouts and state updates is what gives us the typing effect.

```tsx
useEffect(() => {
  // If the word is still being typed
  if (subIndex < words[index].length) {
    // Add the next character to the word after a delay
    setTimeout(() => {
      setWord(words[index].substring(0, subIndex + 1));
      setSubIndex(prev => prev + 1);
    }, 150);
  }
  // If the word has finished typing, wait before starting to delete it
  else if (isVisible) {
    setTimeout(() => {
      setIsVisible(false);
    }, 1000);
  }
  // If the word has been deleted, wait before starting to type the next word
  else if (subIndex === words[index].length) {
    setTimeout(() => {
      setIsVisible(true);
      setIndex((prev) => (prev + 1) % words.length);
      setSubIndex(0);
    }, 150);
  }
  // If the word is being deleted
  else {
    // Remove the last character from the word after a delay
    setTimeout(() => {
      setWord(words[index].substring(0, subIndex));
      setSubIndex(prev => prev - 1);
    }, 75);
  }
}, [index, subIndex, isVisible]);
```

**Return the Component**
The animation effect is implemented within the Form component that houses our search functionality. This is also where we find the props of our `TypingSearchBar` component being used to alter the action of the form and change the styling of the `Input`.

The typing animation is not directly tied to user input or form submission, but is purely a visual effect. When users see this animated placeholder text, they can be guided or inspired on what they might want to search for.

The animation effect is achieved by updating the `word` state variable that is passed as a placeholder to the `Input` component. The continuous updating of the `word` variable with the `useEffect` hook allows us to show the typing, deleting, and word-changing animation within the placeholder of the `Input` component. The Form component's submission functionality remains unchanged and will trigger a search based on the user's input, not the animated placeholder text.

```tsx
  return (
    <Form
      method="get"
      action={params.locale ? `/${params.locale}/search` : '/search'}
      className="flex items-center gap-2 w-full"
    >
      <div className="w-full">
        <Input
          className={
            isHome
              ? 'focus:border-contrast/20 dark:focus:border-primary/20'
              : 'focus:border-primary/20'
          }
          type="search"
          variant={variant}
          placeholder={word}
          name="q"
        />
      </div>
      <button
        type="submit"
        className="relative flex items-center justify-center w-8 h-8 focus:ring-primary/5"
      >
        <IconSearch />
      </button>
    </Form>
  );
}
```

This then leaves us with this code for the full `TypingSearchBar` Component:

```tsx
import { Form } from '@remix-run/react';
import React, { useState, useEffect } from 'react';
import { Input, IconSearch } from '~/components';

export function TypingSearchBar({
  isHome,
  params,
  variant
}: {
  isHome: boolean;
  params: { locale?: string };
  variant: 'search' | 'minisearch';
}) {
  const words = ['Inspire', 'Search'];
  const [index, setIndex] = useState(0);
  const [subIndex, setSubIndex] = useState(0);
  const [word, setWord] = useState('');
  const [isVisible, setIsVisible] = useState(true);
  const displayDuration = 3000;
  const idleDuration = 250; // 1 second

  useEffect(() => {
    if (subIndex < words[index].length) {
      const timeout = setTimeout(() => {
        setWord(prevWord => prevWord + words[index][subIndex]);
        setSubIndex(prevSubIndex => prevSubIndex + 1);
      }, 150);
      return () => clearTimeout(timeout);
    } else if (word.length > 0 && isVisible) {
      const timeout = setTimeout(() => {
        setIsVisible(false);
      }, displayDuration);
      return () => clearTimeout(timeout);
    } else if (!isVisible && word.length > 0) {
      const timeout = setTimeout(() => {
        setWord(prevWord => prevWord.slice(0, -1));
      }, 100);
      return () => clearTimeout(timeout);
    } else {
      const timeout = setTimeout(() => {
        setSubIndex(0);
        setWord('');
        setIsVisible(true);
        setIndex(prevIndex => (prevIndex + 1) % words.length);
      }, idleDuration);
      return () => clearTimeout(timeout);
    }
  }, [subIndex, index, word, isVisible]);

  return (
    <Form
      method="get"
      action={params.locale ? `/${params.locale}/search` : '/search'}
      className="flex items-center gap-2 w-full"
    >
      <div className="w-full">
        <Input
          className={
            isHome
              ? 'focus:border-contrast/20 dark:focus:border-primary/20'
              : 'focus:border-primary/20'
          }
          type="search"
          variant={variant}
          placeholder={word}
          name="q"
        />
      </div>
      <button
        type="submit"
        className="relative flex items-center justify-center w-8 h-8 focus:ring-primary/5"
      >
        <IconSearch />
      </button>
    </Form>
  );
```

We can now simply call this component anywhere we want in our project simply by importing it and passing the specific props.
In our Shopify Hydrogen theme, this is what it would look like:

```tsx
import {useParams} from '@remix-run/react';
import { TypingSearchBar } from '~/components/TypingSearchbar';


export default function Search() {
	const params = useParams();

	return(
    	<TypingSearchBar isHome={false} params={ params } variant="search"/>
    );

}
```

## Wrapping up
In this article, we've taken a deep dive into creating an animated typing search bar using React and Remix. This interactive element not only serves an aesthetic purpose but also encourages user engagement on your e-commerce site. We’ve walked through the code step by step, understanding the key principles behind the implementation. 
If you're curious to know how to implement this in vanilla JS and HTML, just let us know in the comments and we'll be sure to create a guide for that too!
We hope this tutorial sparks your interest and paves the way for further exploration. 
Happy coding!