Building an Instagram Replica from Scratch
Published: 2022-02-02 22:18:45

**TL;DR**: [App](https://imitategram.vercel.app/) - [Source](https://github.com/alshakerM/my-imitategram).

---

Designing a decent, useful app is a tough task, so I decided to outsource the design task and use an existing app. I did some research and decided to go for [Instagram](https://www.instagram.com/).

I decided to document this experience, the lessons I learned, the challenges I faced. Naturally, I underestimated the size of this undertaking, and Instagram turned out to be full of UI quirks and difficulties.

## Quick statistics

<table>
  <tr>
   <td>Commits
   </td>
   <td>Issues
   </td>
   <td>PRs
   </td>
   <td>PR comments
   </td>
  </tr>
  <tr>
   <td>418
   </td>
   <td>30
   </td>
   <td>77
   </td>
   <td>566
   </td>
  </tr>
</table>

I picked Instagram for the following reasons:

1. It’s an excellent exercise of a real product that is used by millions, yet not impossible to replicate for a single developer.
2. It has many interesting things to learn. SVG animations, carousels, chat UI, and lots of state management and routing.
3. It makes for a good demonstration of my skills.

### Technologies I used

#### Boilerplate and hosting

I used [Next.js](https://nextjs.org/) and [Vercel](https://vercel.com/wattx); they were absolutely outstanding for my education journey.

#### State management

My brother introduced me to [WordPress data stores](https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/). They were superb, made my life a lot easier. Out of the box, they give you Redux without the boilerplate, `redux-saga` for your asynchronous needs, they also give you a tremendous set of hooks to access your data. And they make API requests [super clean and nifty](https://github.com/alshakerM/my-imitategram/blob/master/stores/postStore/resolvers.js). The only caveat is the GPL license, I was forced to open-source the project. This project is meant to be OSS, but it might be an issue for other projects.

#### Styling

For styling, I lean heavily towards pure CSS, but even with my smaller projects, CSS selector collision started to become a pain. So, I opted for CSS modules, and I really like them, thanks @markdalgleish!

#### Icons

I used [**Material UI**](https://mui.com/components/material-icons/) Icons library. Instagram uses many icons and some of them are not available in MUI, so I salvaged the SVGs from Instagram.

### Surprisingly difficult challenges ✨

The ring around the profile picture when the user a story.

<video src="https://user-images.githubusercontent.com/17054134/152245953-a4e40560-830d-4fc2-ba5b-2c4babedc1a0.mov" controls></video>

If you dig through Instagram's source, you'll see that they implement this using canvases. After doing some research, I realized learning SVG is more valuable, so I ended up using SVGs. While I didn't get the exact effect, after a lot of fighting with dash arrays and linear gradients, I got a pretty sweet result. You can check it live [here](https://imitategram.vercel.app/dhardstaffh).

SVGs are super powerful, but they were counter-intuitive.

#### Stories Interface

This one was an epic saga. It deserves a post on its own. It has two sets of challenges:

#### Mobile stories and virtualization

This was a really fun one. Before I explain it, let me show you:

<video src="https://user-images.githubusercontent.com/17054134/152240340-cac8237b-eea2-46a7-aa39-96c8393ad8ae.mp4" controls></video>

I had to construct a box with each face representing a user's set of stories. Then, when you drag, I'd rotate said box around the Y axis proportionally to how far you drag. This comes with a ton of state management to pause/start/mute/unmute the correct story at the right time.

It became quickly apparent, the performance was terrible. So, I had to virtualize the story elements. And now, on mobile, I only render three sets of user stories at a time, the previous one, the current, and the next one. Naturally, the edge cases had to be taken care of, e.g.: first story (no previous), last story (no next).

##### Desktop


<video src="https://user-images.githubusercontent.com/17054134/152240727-0eae6122-dc7c-4a75-8846-e86272d80c5e.mp4" controls></video>

On desktop, the stories had to be responsive, centered, and animated. This took a mix of responsive CSS and a sprinkle of JavaScript. I fought hard to keep it pure CSS, but some parts were impossible. Note that Instagram fully relies on JS to lay out the stories UI. And their stories UI on desktop is more buggy than my version :nerd_face:

#### The post carousel 
##### On mobile
<video src="https://user-images.githubusercontent.com/17054134/152243196-9d2b9554-4f2d-49fa-847a-b2a663efd95b.mp4" controls></video>

As an exercise, I decided to implement the carousel manually without a library, and I ended using a mix of CSS native snap-scroll on mobile (free dragging support), and translation with `overflow:hidden` on desktop. The snap-scroll UX turned out to be terrible on desktop (thanks Safari!), so I kept the translation transform there. It's also not perfect on mobile, but does the job.
##### On desktop
<video src="https://user-images.githubusercontent.com/17054134/152242950-4c7ccc23-48ff-4ce2-b684-d567513af726.mov" controls></video>

#### Routing

Instagram uses such a strange routing system; the same URL can mean two different things, if you expand the post by clicking "View all comments" in the feed, you get a floating overlay with the post inside it. If you refresh, you get the independent post page. This was quite tricky to pull off with file-system based routing. It was much easier with React-router, though before I migrated to Next.js. I'm still not happy with the shape of my [CatchAll](https://github.com/alshakerM/my-imitategram/blob/master/pages/%5B%5B...segments%5D%5D.js) component.

#### [Google Safe Browsing](https://safebrowsing.google.com/)

Sadly, after months of work, Google marked the site a phishing site the moment I deployed it! I didn't share it anywhere and merely viewed it! So, I had to implement a disclaimer page and render it before the app to clear any confusion. And I had to remove any trace of the word Instagram from the entire codebase. But I like this page, the cat's eyes follows your cursor. 😸

#### Unique HTML IDs

I really needed the upcoming [`useId`](https://github.com/reactwg/react-18/discussions/111) hook, because my SVG masks and gradients all needed unique IDs. And using random strings messed up the SSR hydration. I ended up writing hacky keys for every SVG mask. Things like `mask-${post.id}`. E.g [here](https://github.com/alshakerM/my-imitategram/blob/fix-dialog/components/Icons/CircularChevron.js#L15).

#### Dealing with [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API)

This was one was more interesting than hard. Instagram marks every post that has tags with an icon in the bottom-left corner, to grab the user's attention, they only show this icon as the post enters the viewport. I had to use IO to make that happen. Here is how it looks:

<video src="https://user-images.githubusercontent.com/17054134/152241204-c61defe4-de8e-4c6c-b291-4bf7003d1a83.mp4" controls></video>

### Lesson to take home

Every app seems much easier when you look at it from the outside. I thought Instagram UI would be a one-month effort, but I decided to do every detail right. And to test on Desktop on Mobile on every step. I definitely need to rethink my estimations!

### Thank you
PR reviews and guidance were patiently given by @alshakero. :heart: