How I built a mini Invision using React
A s̶t̶e̶p̶ ̶b̶y̶ ̶s̶t̶e̶p̶ component by component guide
I love to build things and try new technologies. This time, I wanted to do something different than a Todo app. So, instead of building another Todo list, I decided to go deeper and prototype an app like Invision that lets you prototype Todo apps.
I decided to name the prototype Nightvision and implemented the minimum amount of functionality required to make something that kinda looks like the original app. This is what I ended up with:
To develop Nightvision, I used the same component-centered approach that I use for my other frontend projects:
- Component hierarchy
- Static components
- Dynamic containers
This approach is nothing new and is based primarily on Thinking in React and Pure UI. If you have not read those articles before, stop reading this post right now! Go and read those articles, they will really enhance the way you think about building frontends. Are you done reading that? Great, then let’s start with nightvision’s masterplan.
Before any design happens, I needed to determine the scope of the project. These are nightvision’s requirements I came up with:
- Welcome the user with a splash page
- Show a list of predefined screens that belong to a project
- When the user clicks a screen, he should see that screen in view mode
- Once in view mode the user should be able to go to build mode. This mode lets him draw hotspots on the screen and link those hotspots with other screens
- The user can navigate the project when he is in view mode by clicking the hotspots he generates
It’s always nice to know what we’ll build before starting, right? Now let’s talk design.
Once the scope is clear, I started to design every page. The design process was easy since it was mostly a reduction process. That means I created every screen by cloning Invision and removing everything I did not need. I finished the design after removing a bunch of stuff that I did not want, need or had the time to implement.
I fired up my UX prototyping tool and finished mocking up all screens. These screens are a key part of the development process because they will determine what components I need to create and also shape the application’s data model.
After looking at the screens, I came up with a data format that represents the app. This is just an initial guess, but it’s important to start with an educated guess instead of a shapeless blob of data.
3. Component hierarchy
Static components, aka stateless components, aka pure components are the building blocks of react applications. They serve as reusable elements that you combine piece by piece to create an app. In this step, I decided which visual elements should become components, gave them a name and placed in a component hierarchy.
The first screen is a simple one. It is just a Splash page with a Logo inside.
The second screen has much more elements that can become components. The obvious one is the Project page which will contain the rest of the components. The other container components are Header and Body. Header contains a Logo and a Title, and Body has several ScreenThumbnails.
The third screen has a Screen component, which holds a ScreenImage and a Footer. ScreenImage only has a Hotspot component, while Footer has a Breadcrumb which will show the current routes and two Mode buttons.
What a nice looking component tree! Once that is done, it’s finally time to write some codes.
4. Static components
After the design step, there are 3 main components, we’ll call them pages. We’ve got Splash page, Project page and Screen page. I’ll talk about each one of them and show you links to a working version of them.
Splash page is a full screen dark blue page with a centered logo.
To make it cover the entire viewport I used css to set its width to 100vw (viewport width units) and height to 100vh (viewport height units).
Dark blue background
Since we are using styled jsx as our component based CSS solution, we can easily store our app’s colors in a js file and interpolate those values onto our component. This means we can have our colors neatly organized and will never have to copy paste a color hex number again.
To create the Logo, I used the logo I created in the design step, exported it to svg, optimized it with SVG Optimizer and then used its markup in a React component. Now I can easily parameterize the logo using JSX!
In the Project screen I created a Header with a Logo and title, and a ScreenThumbnail.
There is really not much to say about the Header, other than it reuses the Logo component created earlier.
It also has a Screens component that iterates over the available screens and renders a Screen component. This Screen component shows the screen image, its title and a timestamp.
In this project, I wanted to use the brand new React Router 4, so I went ahead and did a npm install react-router-dom — save. Defining the routes was super easy, I only needed to wrap Screen and Project inside a Route. I wanted the Splash page to redirect to Project after 2 seconds, so I created a RedirectTimeout component that does just that, it wraps a component and you tell it how much time to wait and which route it should render after that period. App.js looks like this now:
Adding Screen detail page, allows the user to click a screen inside Project and see it in view mode or build mode. Implementing it was trivial, I had to lift state from Project to App because Screen also needed it.
Screen has only one state variable, state.mode which can be either build or view. It affects both its subcomponents. Toolbar highlights ViewButton or BuildButton depending on the current mode, and ScreenImage lets you navigate your project if mode equals view, or it lets you draw hotspots in build mode.
This is how the render method of Screen looks like:
Even though mocking up Screen did not have much to it, the next step will be a fun one because I’ll add the ability to draw hotspots in a screen.
Hotspots are sections of pages that link to other pages. You can create them in build mode and navigate them in view mode. To let the user create hotspots, I created a HotspotDrawer component that gets shown in build mode. To show you how it works, take a look at its render method:
Internally it always renders a Hotspot in build mode. By default, this hotspot has width and height set to zero, so it is not visible. When you click the screen you call the function this.setXY which sets the hotspot origin. When you move your mouse (or finger obviously) this.calculateHotspotPosition determines the width and height of the hotspot, which makes it visible. Fiinally when you release this.onHotspotDrawn gets called. This function shows the modal that lets the user link the hotspot to another screen.
This was the last component that the app needed to fulfil its initial requirements!
5. Dynamic containers
This app was so simple that I decided not to separate components between presentational and container components. Rules are made to be broken, right? Well, despite not making container components, I did store all state in the root App component and made the rest of the components presentational. That means they receive the app state and receive functions that modify that state as props.
Congrats! You made it through all of those components. The final version of Nightvision is available at https://nightvision.now.sh, go check it out. If you are feeling adventurous, check out its source code in github.