[flaviocopes] The React Course - React Pixel Drawing App

Use React to build a pixel art drawing app. A fun app yet instructive to build!

I recommend to keep those resources of mine
at hand while following the course:

Introduction the Pixel Drawing app

You’ve probably seen a similar application already. I have a few of them on my iPad. It’s a cool way to relax, by drawing something cool.

This is what we aim for:

The app will run in the browser and will use React as its foundation.

What we’ll build in detail

The goal of this tutorial is to build a canvas, a 30x30 “pixels” matrix. When the app starts, there is a blank canvas showing up.

There is also a color picker, which shows the colors you can use. A default color is pre-selected when the app starts.

When you click one pixel in the canvas, that pixel will be colored with that default color.

The main components of the app

Given the UI that is showed above, we basically need 3 components.

  1. One is the Pixel , a single unit that will compose our drawing.
  2. One is the Canvas , which is a set of 30 rows, each containing 30 Pixel components.
  3. The last one is the ColorPicker , which contains one or more Pixels components.

Clicking a Pixel component inside a ColorPicker will change the active color.

Clicking a Pixel component inside a Canvas will change the background color of the pixel.

Bootstrap a React app using create-react-app

We can now start creating our app.

I’m going to use CodeSandbox in my tutorial because it’s built upon create-react-app . You can choose to develop locally, or use CodeSandbox.

The choice is yours.

If you choose to work locally, just start a new React app by using the command npx create-react-app pixelart-react , then go into that folder and run npm run start . This is what you should see:

Here is the link to create a new CodeSandbox project: https://codesandbox.io/s. You just visit the page, click React and start building things right away. There are many other options, and CodeSandbox is an awesome tool for all things JavaScript.

Wrapping up

You now know what we’re going to build. It’s key to have the final goal in mind when working on something.

In the next lesson we’ll create the first React component of the app.

The first component, Pixel

In the previous lesson you found out what we’re going to build: a pixel coloring app!

Here we’ll create the first building block of the application. The smallest unit: a pixel .

Create a components folder in the src folder, and inside it create an empty Pixel.js file.

We’re going to import React, then we create a function component that simply outputs a div with two classes: pixel and the background property passed as a prop:

import React from 'react'

export default props => {
  return <div className={`${props.background} pixel`} />
}

I use an HTML class to determine the look of the component. Remember that React outputs any value found in the className attribute in JSX as the HTML class attribute. The name differs from “regular HTML” because class is a reserved word in JavaScript.

I style those classes using CSS, in src/styles.css , which is already existing, I define 4 colors, which I’ll use as the base for the app example:

.white {
  background-color: white;
}
.lightblue {
  background-color: rgb(0, 188, 212);
}
.blue {
  background-color: rgb(3, 169, 244);
}
.darkblue {
  background-color: rgb(33, 150, 243);
}

Where are these 4 colors defined? I define them in a Colors.js module, because they are used in various places.

export default ['white', 'lightblue', 'blue', 'darkblue']

The first color is the base color, and defines the color used by all cells when the app starts. The others are 3 choices I give to color the pixels.

Now let’s just add this one pixel in the app. Open src/index.js and after all the imports add

import Pixel from './components/Pixel'

and inside the App() function, make the component return one Pixel:

return (
  <div className="App">
    <Pixel />
  </div>
)

Let’s add a bit of styling to src/styles.css to make our Pixel actually appear on screen, by setting its width/height, and the border:

.pixel {
  width: 30px;
  height: 30px;
  border: 1px solid lightgray;
  box-sizing: border-box;
}

In your browser now you will see a little pixel showing up:

Wrapping up

In the next lesson we’ll combine this component and replicate it to create a grid of pixels.

Create the Canvas component

In the previous lesson we created the first component of the application.

Now we’ll take that component and use it as a base for a grid of pixels, which will be the canvas of our application. Canvas , therefore, seems a good name for the new Component.

Create an empty file in src/components/Canvas.js .

We want the canvas to show a 30x30 grid composed by Pixel components.

The component stores this grid in its state.

How we construct this grid? This piece of JavaScript creates a row of 30 elements:

Array(30).fill()

I want it to be initialized with 0 , so they all default to the our default color, so I use map() for this:

Array(30)
  .fill()
  .map(() => 0)

This works for one row.

For one single row, I could pass 0 as a parameter to fill() :

Array(30).fill(0)

I want 30 rows, so I can just repeat this process for the rows as well:

const matrix = Array(30)
  .fill()
  .map(() =>
    Array(30).fill(0)
  )

This is our matrix.

We initialize it in the Canvas component, and use it to initialize the matrix state property using the hooks useState() function:

import React, { Component, useState } from 'react'
//...
const Canvas = () => {
  const [matrix, setMatrix] = useState(Array(30)
    .fill()
    .map(() => Array(30).fill(0)))
}

We are using what we previously called const matrix as the initial value of our matrix.

setMatrix is the function that we’ll use to update the matrix later on.

Now we can render this matrix with the render() method. I use map() on the matrix array, to iterate every row, and for each row I do the same for every column, and return a Pixel component for every single element in the matrix:

import React, { Component, useState } from 'react'
import Colors from '../Colors'
import Pixel from './Pixel'

const Canvas = () => {
  const [matrix, setMatrix] = useState(Array(30)
    .fill()
    .map(() => Array(30).fill(0)))

  return (
    <div className={'canvas'}>
      {matrix.map((row, rowIndex) =>
        row.map((_, colIndex) => {
          return (
            <Pixel
              key={`${rowIndex}-${colIndex}`}
              background={Colors[matrix[rowIndex][colIndex]]}
            />
          )
        })
      )}
    </div>
  )
}

export default Canvas

as usual when outputting components in a loop, we add a key prop (Why? Because React wants it for performance purposes - to render things faster inside our list).

The background prop makes sure we add the color name to the Pixel. Since the matrix stores the color index (an integer from 0 to 3 in this case), we import the Colors array and get the name of the color from it.

Tip: we’re not using the value returned by the map() function, the second time I call it, so I assign it the value _ , to mean I don’t actually care about it. It’s just a convention (which I borrowed from the Go programming language, where _ actually means we ignore the parameter).

Now let’s go back to the src/index.js file, and instead of including and then adding the Pixel component to the output, we do do the same with the Canvas component:

//...
import Canvas from './components/Canvas'

function App() {
  return (
    <div className="App">
      <Canvas />
    </div>
  )
}
//...

The last thing we need is to add some CSS to the src/styles.css file to style our canvas and App classes:

.App {
  background-color: #333;
  display: flex;
  justify-content: center;
  align-items: center;
  height: 100vh;
}

.canvas {
  display: flex;
  flex-wrap: wrap;
  max-width: 900px;
}

Cool! We can now see the canvas:

Wrapping up

So we went from displaying a single Pixel to composing multiple Pixel components into a bigger Canvas component. So far, so good!

In the next lesson we’ll add a property to the Pixel component to make it have a color!

See the app on CodeSandbox

The ColorPicker component

In the last few lessons we created a Canvas, composed by 900 pixels, and we made the pixels colorable using a background prop.

Now we’ll work on a ColorPicker component, which will allow us to choose between 4 different colors, and as such will be composed by 4 pixels.

Let’s create a new file in src/components named ColorPicker.js .

This will host our ColorPicker component.

The ColorPicker component as said in the introduction will simply host 4 instances of the Pixel component.

Everyone will have a different color. In the next lesson we’ll make it do something useful, but for now, we just need to display it correctly and add it to our app in the correct position.

So we start from zero:

import React from 'react'
import Pixel from './Pixel'

export default props => {
  return (
    <div className="colorpicker">
    </div>
  )
}

and we add 4 Pixel components by iterating over the Colors array, which is defined in the src/Colors.js file:

//...
import Colors from '../Colors'

export default props => {
  return (
    <div className="colorpicker">
      {Colors.map((color, index) => {
        return <Pixel key={index} background={color} />
      })}
    </div>
  )
}

We pass a background prop with a different color for each pixel, so we have 4 pixels, all colored differently. We can add new colors simply by adding a new entry in the src/Colors.js file.

Now we can import the ColorPicker component in the src/index.js file and render it in the template:

import React from 'react'
import ReactDOM from 'react-dom'

import './styles.css'
import Canvas from './components/Canvas'
import ColorPicker from './components/ColorPicker'

function App() {
  return (
    <div className="App">
      <ColorPicker />
      <Canvas />
    </div>
  )
}

const rootElement = document.getElementById('root')
ReactDOM.render(<App />, rootElement)

Here it is in the browser preview:

Wrapping up

We wrapped 4 Pixel components in a ColorPicker component, assigning to each one of them a different color.

In the next lesson, we’ll change a property in the App component state when one of those pixels is clicked, to set the currently selected color.

See on CodeSandbox

Let the user choose a color

In the previous lesson we introduced the ColorPicker component.

Now we put that component to good use. When the user clicks the button we’ll change the default color of the application. This will be in turn used in the next lesson when we’ll allow to color pixels by clicking with the mouse.

The user must be able to click one of the colors in the color picker, and select it to draw something.

First, let’s make the default color highlighted when the app starts.

In src/index.js we store the current color in the color property of the state:

import React, { useState } from 'react'

//... in App()
const [color, setColor] = useState(0)

Let’s pass it down to the ColorPicker component:

<ColorPicker currentColor={color} />

Now inside the ColorPicker component, we use this prop to identify the current color:

<Pixel
  key={index}
  background={color}
  current={Colors[props.currentColor] === color}
/>

Every pixel now will get a current prop that’s either true or false .

If it’s true, we’re going to add a current-color class to the Pixel component, in src/components/Pixel.js :

export default props => {
  return (
    <div
      className={`${props.background} pixel ${
        props.current ? 'current-color' : ''
      }`}
    />
  )
}

which we style in src/styles.css to make it highlighted, have a bigger border:

.pixel.current-color {
  border: 4px solid yellow;
}

Cool! Now we need to let the user change this color.

When the Pixel component in the ColorPicker is clicked, we must update the currently selected color. We start by adding an onClick property to the Pixel JSX:

export default props => {
  return (
    <div
      className={`${props.background} pixel ${
        props.current ? 'current-color' : ''
      }`}
      onClick={props.onClick}
    />
  )
}

which calls a function that’s passed down by the parent component, ColorPicker :

<Pixel
  onClick={e => props.setColor(index)}
  key={index}
  background={color}
  current={Colors[props.currentColor] === color}
/>

See, we pass an arrow function to onClick which calls the props.setColor() function, which is passed by the App component.

In App.js we add setColor() and we pass it to ColorPicker as a prop:

function App() {
  const [color, setColor] = useState(0)

  return (
    <div className="App">
      <ColorPicker currentColor={color}
        setColor={color => setColor(color)} />
      <Canvas />
    </div>
  )
}

We can now choose another color from the list, and that will be the new default!

Wrapping up

In this lesson, probably the most complex of this module, you first added a centralized place to store the current color in the App component.

Then you set up an event system to handle changing the color when a Pixel in the ColorPicker is clicked.

Let’s go to the next lesson to start coloring the pixels in the canvas!

See on CodeSandbox

Color a pixel when clicked

In the previous lessons we started with a simple Pixel component, we grouped up 900 Pixel components in a Canvas component, and we used 4 Pixel components in a ColorPicker component.

We made it possible to color Pixels, and to have a set of colors to choose from. We also set a default color.

Now let’s add the last bit of functionality: we want to color pixels in the canvas, with the color selected in the ColorPicker .

When I click a pixel, that’s going to transform its background color to the currently active color in the color picker.

The Pixel component already has an onClick handler, because we added it when we wanted to change the default color in the ColorPicker. Luckily this is just calling the parent prop, so we can pass our handler in the Canvas component:

//...
const Canvas = props => {
  //...

  const changeColor = (rowIndex, colIndex) => {
    //...
  }

  return (
    <div className={'canvas'}>
      {matrix.map((row, rowIndex) =>
        row.map((_, colIndex) => {
          return (
            <Pixel
              key={`${rowIndex}-${colIndex}`}
              background={Colors[matrix[rowIndex][colIndex]]}
              onClick={e => changeColor(rowIndex, colIndex)}
            />
          )
        })
      )}
    </div>
  )
}

The changeColor() method is what actually changes the color of a pixel. It’s in the Canvas component because that’s where the state of the whole set of pixels (the matrix variable) is kept.

Let’s implement it.

We clone the matrix nested array, by using this kind of strange way: JSON.parse(JSON.stringify(my_array)) so that we don’t go and change the previous array, but we create a new one. Why we do so? For immutability reasons. We can’t change the matrix implicitly by editing its current value, but we want it to only change through our setMatrix() call.

After cloning the matrix we then update the specific location given by rowIndex and colIndex and we set it to the new color, and after this we set the new state:

const changeColor = (rowIndex, colIndex) => {
  const newMatrix = JSON.parse(JSON.stringify(matrix))
  newMatrix[rowIndex][colIndex] = props.currentColor
  setMatrix(newMatrix)
}

currentColor is passed by the App component when it includes Canvas, like it already does for ColorPicker:

function App() {
  const [color, setColor] = useState(0)

  return (
    <div className="App">
      <ColorPicker currentColor={color} setColor={color => setColor(color)} />
      <Canvas currentColor={color} />
    </div>
  )
}

We are done!

Here is the result:

And here is the full app in CodeSandbox for you to try out if you missed a step: https://codesandbox.io/s/7wq3pqv9oq

Wrapping up

We finally finished our little application. We can now start playing it to draw pixel art all day long :smile:

. . .

I’m joking. Time to go to the next lesson, to wrap up the module!

What we learned

In this module we wrote a nice little application with React, that lets you draw pixel art with the mouse.

We started from a problem, we identified the units called components we needed, we combined them, and we explored a few of the possibilities that React gives us to build very cool applications.

Challenges for you

You can start by replicating the project on your own. Try from scratch, and follow the lessons to know what to do, but type every single letter yourself. No copy/pasting.

Once you do this, you can start expanding the project a little bit.

Here are the challenges:

  1. You can store your progress with the drawing in local storage, so when you reload the page, you didn’t lose all your changes.
  2. Allow to save different drawings to local storage, and start from a pre-existing drawing you saved previously
  3. Use Styled Components to style the app
  4. You can assign a number to each color in the legend, and instead of having a blank canvas in the beginning, have a pre-built drawing with those numbers. In this way you know where to click to make a nice drawing without having to come up with an idea.

You might find a stumbling block while working on those project expansions. If you do, contact me and we’ll find a solution.