Demo • Features • Technologies Used • Screenshots • Problem Solving • Code Examples • Future Improvements
https://jamcmich.github.io/google-search-clone/
Note: This website has identical elements and styling to Google's search page but includes only the essential search features.
- Search functionality with Google via RapidAPI
- Responsive styling for mobile, tablet, and desktop
- Data fetching and theme switcher via Higher Order Components
- Custom tooltip system
This project was my first exposure to using WindiCSS (an on-demand alternative to Tailwind). Although Windi is convenient for styling elements on the fly, it suffers from the same issues as Tailwind: lack of legibility, inability to chain selectors, cluttering the DOM, etc. I realized the need for a consistent naming scheme after styling my first few React components and began researching a flexible solution.
After looking into the issue, I discovered a CSS naming convention created by BEM for writing cleaner and more readable class names. You can read up on the methodology with examples in BEM's official guide. These practices provided me with solutions for simplifying the DOM structure, creating descriptive CSS styles, and self-documenting my code.
Reading up on the Windi documentation allowed me to incorporate Windi's utility classes in an external stylesheet and preserve my application's class naming schemes. Even though this method would invalidate one of the framework's biggest advantages, I was able to reduce the clutter of my JSX files and create a solution with BEM conventions in mind.
Since React is a very flexible library, many developers turn to third-party solutions when it comes to certain features. For example, some components on Google's search page provide users with tooltips on hover and I needed a way to incorporate this functionality into my project.
React Tooltip is a library with almost 3k stars on GitHub. Many developers rely on external solutions such as this one assuming the library will be well maintained and up-to-date. Unfortunately, myself and many others were running into compatibility issues between React Tooltip and React 18. The repository had been struggling to find a maintainer and likely wouldn't be updated for the new version of React anytime soon.
I decided to create a simple, reusable tooltip system that complimented my project instead of relying on the inconsistencies of an external library. This solution was lightweight and allowed me to wrap icons within my <Tooltip />
component and create custom tooltip styles.
For this application, we only need to service GET requests from the API. Fetching data in React is fairly straightforward using Async/Await and Axios (see Data Fetching code snippet). The real challenge is distributing the returned data across a React project between parent and child components. To overcome this obstacle we can use React Context.
The React Context API was introduced in React v16.3, allowing developers to pass data through component trees and share information at various levels in an application. Instead of passing props down through every single component on the tree, the components that require a prop can simply request it from useContext()
. This feature circumvents the dreaded practice of prop drilling and reduces unnecessary re-rendering.
To use Context within our application we first establish a State Provider, essentially a higher order component that passes values to it's {children}
, to use state within our application. Feel free to view my implementation of StateContext here for a better understanding. You might notice that my code includes an additional implementation useReducer()
which is an alternative to useState()
and is used when the next state depends on the previous one. This allows us to implement features such as comparing form inputs or toggling themes.
I used React's reducer hook in conjunction with React Context to reference search inputs and the application's current theme. useReducer()
works exactly like JavaScript's Array reduce()
function by receiving an initial value of state
and an action
, and then returning a new state. I created two actionTypes
within my application, SET_SEARCH_INPUT
and SET_APPLICATION_THEME
, to pass input and theme values to various components (see Reducer code snippet).
Data is rendered to the virtual DOM by mapping over results fetched from the GET method in useSearch.js
.
React Context makes it easy to receive, mutate, and pass the global state value of theme
to any component. In this example with the application's theme switching feature, I use the theme
state to conditionally render icons and toggle between styles pertaining to light and dark mode.
Search engines are built with varying degrees of complexity to allow for detailed queries. Most users take advantage of basic keyword searching so I decided to implement a regex pattern that combines words and filters special characters.
For example, searching cat memes
or cat!@#$%-_memes
is parsed to cat+memes
which is the appropriate format for Google's search engine parameters. The resulting URL becomes https://google-search3.p.rapidapi.com/api/v1/search/q=cat+memes&num=10
.
This project was my first exposure to GitHub Actions. After reading about the benefits of continuous integration and development, I decided to create a workflow that would build and deploy my project to GitHub Pages automatically.
A neat trick I learned throughout the development of this project is using Webpack aliases to create shortened file references. This is extremely helpful for avoiding long and repetitive path names such as import ModuleName from '../../../src/components/Component
. See the Webpack Configuration code snippet for more details.
- Pagination
- "I'm Feeling Lucky" button takes the user to a random page
- Sorting results into sections (i.e. All, Images, News, etc.)