# Create a Custom Chrome History Extension with React - Part 2

> Learn how to build a custom Chrome history extension with React in this guide. Explore the Chrome History API and improve your browsing experience.

- Author: Marius Bongarts
- Published: 2023-03-10
- Updated: 2024-07-23
- Tags: Chrome Extension, React, Tutorial, Web Development
- Reading time: 5 min
- URL: https://web-highlights.com/blog/build-your-own-custom-chrome-history-extension-with-react-part-2-chrome-history-api/

---

In this series of articles, I will walk you through creating your own browser history Chrome extension using React. We will use the **Chrome History API** to read and display our browser history in our personalized React dashboard.

In the first part, we learned how to set up a Chrome extension using React and Webpack. The setup from Part 1 is our ground base to continue with the **Chrome History API** integration. Therefore, check out the first article if you haven’t:

> [**Build Your Own Custom Chrome History Extension with React - Part 1: Set Up**](https://web-highlights.com/blog/build-your-own-custom-chrome-history-extension-with-react-part-1-set-up/) — Step-by-Step Guide to Setting Up Your Custom Chrome History Extension in React. Learn how to create a Chrome extension with React and how to implement the Chrome History API (Marius Bongarts)

Again, here is what we are going to build:

![Our own Chrome History — What we will build](https://cdn-images-1.medium.com/max/1600/1*ldEieDkorqouo1mb7UBwNg.png)

*Our own Chrome History — What we will build*

### Typescript Setup

Before we start, we need to configure Typescript to give us autocompletion for the chrome APIs.

Try it yourself and type `"chrome.”` into your IDE. Usually, you should not see any autosuggestions.

Therefore, you need to install the typings for the chrome APIs to access them. You can do this by running:

_yarn add -D @types/chrome_

Now, you need to add this into the `tsconfig.json` file:

```json
{
  "name": "Chrome History Extension",
  "version": "1.0.0",
  "manifest_version": 3
}
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/19c5a920705cd19a60dff01005663dec)

Finally, you should get autosuggestions from your IDE:

![Chrome API Typescript suggestions](https://cdn-images-1.medium.com/max/1600/1*b4R4cdgmvto6aAaEfYa-zg.png)

*Chrome API Typescript suggestions*

### Chrome History API

Our goal is to show the browser history in our dashboard. For that, we need to access the [**Chrome History API**](https://developer.chrome.com/docs/extensions/reference/history/) . Having our types configured, we should get autocompletion when typing `window.history` . To get started, let’s create a search query to get the ten latest entries in our browser history. We can do this by calling it inside our `Dashboard.tsx` component:

```tsx
useEffect(() => {
  chrome.history.search({ text: "", maxResults: 10 }, (data) => {
    console.log(data);
  });
}, []);
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/d8789887e1901ada24b4b0391b566385)

When refreshing the page, we should see an error:

![Uncaught TypeError: Cannot read properties of undefined (reading ‘search’)](https://cdn-images-1.medium.com/max/1600/1*-YAStErQmBHhJZUOETS5zQ.png)

*Uncaught TypeError: Cannot read properties of undefined (reading ‘search’)*

We get an error because `window.history` is `undefined` . That’s because we don’t have permission to use it yet. To fix this, we need to add it to our `permissions` array inside our `manifest.json` :

```json
{
  "name": "chrome-history-extension",
  ...
  "permissions": ["history"]
}
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/7a471766ead24a88d2ada560dc78cbd8)

After reloading our extension, we should see an array of our ten most recent browser history entries in the console.

> [**Building Your Own New Tab Chrome Extension**](https://web-highlights.com/blog/building-your-own-new-tab-chrome-extension/) — How to build a beautiful New Tab Chrome Extension with plain HTML, CSS, JavaScript, and Web Components (Marius Bongarts)

Before we show those entries in our dashboard, let’s make the history search call more accessible by providing a more straightforward interface using a custom React hook.

Let’s create a `useChromeHistorySearch` hook that executes a history search query with the provided `HistoryQueryObject` and sets the `historyItems` in a state, once the query finishes:

```tsx
type HistoryItem = chrome.history.HistoryItem;
export const useChromeHistorySearch = (
  query: chrome.history.HistoryQuery
): HistoryItem[] => {
  const [historyItems, setHistoryItems] = useState<HistoryItem[]>([]);
  useEffect(() => {
    chrome.history
      .search(query)
      .then((historyItems) => setHistoryItems(historyItems))
      .catch(() => setHistoryItems([]));
  }, [query]);
  return historyItems;
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/9902738c2ca670644d259c2fdfe1ff75)

Having this helper hook makes it easier to handle the asynchronous `chrome.history.search()` function within our components.

Now, finally, let’s show those entries in the frontend.

### Display Browser History

Now that we can get our data, let’s show it in our browser. We will build three components for this. The `Dashboard.tsx` component will use the `useChromeHistorySearch` hook to pass the data to our `History.tsx` component.

```tsx
import React from "react";
import { useChromeHistorySearch } from "../hooks/useChromeHistorySearch";
import { History } from "./History";

interface DashboardProps {}

const query = { text: "", maxResults: 100 };

export const Dashboard: React.FC<DashboardProps> = () => {
  const mostRecentItems = useChromeHistorySearch(query);

  return (
    <div>
      <h1>Chrome History Dashboard 📊</h1>
      <History items={mostRecentItems} />
    </div>
  );
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/9435601522c696ed612226d6dd891a46)

Our `History.tsx` component simply takes in an `items` array containing the chrome browser history entries to be shown. Now we iterate over those and show them in an unordered list like this:

```tsx
import React from "react";
import { HistoryItem } from "./HistoryItem";
import { StyledList } from "./History.styled";

const MemoizedHistoryItem = React.memo(HistoryItem);

interface HistoryProps {
  items: chrome.history.HistoryItem[];
}

export const History: React.FC<HistoryProps> = ({ items }) => {
  return (
    <StyledList>
      {items.map((item) => (
        <MemoizedHistoryItem key={item.id} item={item} />
      ))}
    </StyledList>
  );
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/cf672d06385ce6fd9ed240b8dc1ca099)

For each `item` we render a `HistoryItem` which is another component that renders the item’s information which looks like this:

```tsx
import React from "react";

interface HistoryItemProps {
  item: chrome.history.HistoryItem;
}

export const HistoryItem: React.FC<HistoryItemProps> = ({ item }) => {
  return (
    <li>
      <span>{new Date(item.lastVisitTime ?? 0).toLocaleTimeString()}</span>
      {item.title}
    </li>
  );
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/53cceba3d3a7268c6d05d665e43571a9)

Our extension should now look like this:

![Chrome History Dashboard](https://cdn-images-1.medium.com/max/1600/1*l7qJbIchuJ1Xedd8RHGVRg.png)

That’s already great. Now let’s make it look a little better.

### Add Styles

To apply styles to our components, we will use the `styled-components` library that lets us write CSS inside our Typescript components.

> _This means you can use all the features of CSS you use and love, including (but by far not limited to) media queries, all pseudo-selectors, nesting, etc. —_ [_styled-components.com_](https://styled-components.com/)

Let’s install it by running:

_yarn add styled-components @types/styled-components_

Now, we can customize our unordered list like this:

```tsx
const StyledList = styled.ul`
  list-style: none;
  padding: 0;
  margin: 0;
`;
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/49edeffd2b4396ba54a46daaec495902)

This will remove the list dots on the left and also removes some margin and padding. We can use this list in our template like this:

Furthermore, we will style the **date** of each entry to make its font color light grey, and we will have a title and a link to the URL:

```tsx
export const StyledInfo = styled.div`
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  row-gap: 2px;
  padding: 4px;
`;

export const StyledItemLink = styled.a`
  color: var(--light-grey);
  text-decoration: none;
  font-size: 0.8rem;
`;
export const StyledTitle = styled.span`
  color: black;
  width: 100%;
  font-size: 0.9rem;
  font-weight: 400;
`;

export const StyledTime = styled.span`
  color: var(--light-grey);
`;
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/af7557d1acb85e7273c994781c13b94a)

We can use the styled components like this in our template:

```tsx
import React from "react";
import { truncateString, urlWithoutSchema } from "../services/helper";
import {
  StyledInfo,
  StyledHistoryItem,
  StyledItemLink,
  StyledTitle,
  StyledTime,
} from "./HistoryItem.styled";

interface HistoryItemProps {
  item: chrome.history.HistoryItem;
}

export const HistoryItem: React.FC<HistoryItemProps> = ({ item }) => {
  const lastVisitDate = new Date(item.lastVisitTime ?? 0);
  const time = lastVisitDate.toLocaleTimeString().substring(0, 5);

  const url = urlWithoutSchema(item.url ?? "");

  return (
    <StyledHistoryItem>
      <StyledInfo>
        <StyledTitle title={item.title}>
          {truncateString(item.title ?? "", 50)}
        </StyledTitle>
        <br />
        <StyledItemLink href={item.url} target="_blank" title={item.url}>
          {truncateString(url, 30)}
        </StyledItemLink>
      </StyledInfo>
      <StyledTime title={lastVisitDate.toTimeString()}>{time}</StyledTime>
    </StyledHistoryItem>
  );
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/ff9dc92e165a06b7c0747fac7d1f352f)

Those will give our history list some basic styles to make it look like this:

![History Extension with styles](https://cdn-images-1.medium.com/max/1600/1*D5679JssEDniI1UmOe_WvA.png)

*History Extension with styles*

#### **Add Favicon icons**

Now, let’s improve the design of our `HistoryItem.tsx` file a bit more. To make it look better, we will be add a favicon to our history list items. We can do this by using a Google API that automatically pulls the favicon image of any URL.

It works using a simple GET request to:

**_https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback\_opts=TYPE,SIZE,URL&url=${item.url}&size=16_**

We simply replace the `url` parameter to be the history item’s `url` and pass it to an image like this:

```tsx
import React from "react";
import { truncateString, urlWithoutSchema } from "../services/helper";
import {
  StyledInfo,
  StyledHistoryItem,
  StyledItemLink,
  StyledTitle,
  StyledTime,
} from "./HistoryItem.styled";

interface HistoryItemProps {
  item: chrome.history.HistoryItem;
}

export const HistoryItem: React.FC<HistoryItemProps> = ({ item }) => {
  const faviconUrl = `https://t0.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${item.url}&size=16`;
  const lastVisitDate = new Date(item.lastVisitTime ?? 0);
  const time = lastVisitDate.toLocaleTimeString().substring(0, 5);

  const url = urlWithoutSchema(item.url ?? "");

  return (
    <StyledHistoryItem>
      <img
        src={faviconUrl}
        alt={`Favicon of: ${item.url}`}
        width="16"
        height="16"
        loading="lazy"
      ></img>
      <StyledInfo>
        <StyledTitle title={item.title}>
          {truncateString(item.title ?? "", 50)}
        </StyledTitle>
        <br />
        <StyledItemLink href={item.url} target="_blank" title={item.url}>
          {truncateString(url, 30)}
        </StyledItemLink>
      </StyledInfo>
      <StyledTime title={lastVisitDate.toTimeString()}>{time}</StyledTime>
    </StyledHistoryItem>
  );
};
```

[View this code on GitHub Gist](https://gist.github.com/MariusBongarts/212893eb544dde29d4d5124e586eaf53)

Here is what our extension looks like now:

![History Extension with Favicon and styles](https://cdn-images-1.medium.com/max/1600/1*qMTllCXEfdmACM00_oy_FQ.png)

*History Extension with Favicon and styles*

### Final Thoughts

I would say that our extension already looks very good. We have successfully integrated the Chrome History API to show the recently visited pages in our dashboard. Also, we applied some styles to make it look a little better.

In the next part, we will take care of displaying the extensive list of history items efficiently using lazy-loading. We will also integrate a search bar to search for specific entries in our browsing history.

I hope you enjoyed reading this article. I am always happy to answer questions and am open to criticism. Feel free to contact me at any time! Get in touch with me via [**LinkedIn**](https://www.linkedin.com/in/marius-bongarts-6b3638171/) , [**Twitter**](https://twitter.com/MariusBongarts) , or leave a comment.
