---
title: "How to Build a Photo Editor With React and CSS Filters | CoderPad"
description: "In a world of constant development and evolution, especially in the software engineering world, CSS isn’t left behind; it now includes new features that allow developers to apply effects that previously would only seem possible with graphics applications such as Adobe. Learn these new CSS filters by building a photo ed"
canonical: "https://coderpad.io/blog/development/react-photo-editor-with-css-filters/"
last_modified: "2023-06-05 14:10:02"
---
[Back to blog](https://coderpad.io/blog/)

# How to Build a Photo Editor With React and CSS Filters

September 12, 2022 9 Minute Read

By Uma Victor

[Development](https://coderpad.io/blog/development/)

CSS is the language used to control the style of a web document in a simple and easy way. It helps makes a web application look more presentable and visually appealing.

In a world of constant development and evolution, especially in the software engineering world, CSS isn’t left behind; it now includes new features that allow developers to apply effects that previously would only seem possible with graphics applications such as Adobe. These new features are “CSS Filters,” [which we recently covered in-depth on our blog.](https://coderpad.io/blog/development/everything-you-need-to-know-about-all-11-css-filters/)

Text, photos, and other aspects on a webpage can have visual effects applied to them using CSS filters. We can access effects like color, blur, and shifting on an element’s rendering before the element is displayed, thanks to the CSS filter property.

In this article, we will build a photo editor using React and some CSS filters.

Specifically, in this article we’ll learn how to:

- Understand the CSS filter properties with a quick recap
- Set up our React project and start building out our application
- Set up the JSX structure for our project.
- Style our project using standard CSS. This includes our project sidebar.
- Make our sidebar, containing our CSS filters, responsive using JavaScript.
- Add some functionality to our slider using JavaScript
- See the final app in action

## A quick recap of 11 CSS filter properties

Let’s look at some of the CSS Filter properties and what they do:

- blur():`blur()`applies the blur effect to an element. If the blur value is not specified then it takes in`0`as the default value.

The result when adjusted to`70%`blur:

[![The CoderPad logo with a blurring effect applied](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f77992f3.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f77992f3.png) The CoderPad logo is blurred by the CSS filter
- brightness():`brightness()`controls how bright the element is. It is completely black at`0%`brightness, and it is identical to the original at 100% brightness. Elements are brighter when the values are higher than`100%`.

The result when adjusted to`99%`brightness:

[![The CoderPad logo with the brightness increased to the extent that the word "Pad" is no longer visible](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f79b5c10.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f79b5c10.png) The CoderPad logo with the brightness increased to the extent that the word “Pad” is no longer visible
- contrast():`contrast`aids in adjusting an element’s contrast. When the contrast is`0%`, an element is completely black; when it is`100%`, it shows the original element.

The result when adjusted to`70%`contrast:

[![The CoderPad logo with a 70% contrast effect applied](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7ac2897.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7ac2897.png) Contrast is decreased in the CoderPad image, making all colors more contrasted
- grayscale(): The colors of an element are changed to black and white in a grayscale. The grayscale`0%`represents the original element, while`100%`grayscale displays the element in grayscale entirely. Values of`grayscale`in between`0%`and`100%`are a mix of grayscale and color. Negative values are not accepted by grayscale.

The result of 100% grayscale:

[![The CoderPad logo with a grayscale effect applied](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7bce864.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7bce864.png) The CoderPad logo entirely grayscaled. The red in the logo is now a dark gray
- hue-rotate(): The image is given a different color rotation with`hue-rotate()`. The value specifies how many degrees the picture samples will be rotated around the color circle. The default value of 0 degrees represents the original image.

The result when adjusted to`70%`hue-rotate:

[![The CoderPad logo with a hue-rotate effect applied](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7cc64b1.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7cc64b1.png) A hue-rotated CoderPad logo, making the red look like a purple.
- sepia():`sepia()`transforms an image into a sepia image, where`100%`of the image is sepia and 0% is the original image.

The result when adjusted to`70`sepia:

[![The CoderPad logo with a sepia effect applied](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7db361e.png)](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7db361e.png) The CoderPad logo with 70% sepia applied, making the colors more brown
- saturate(): To control an element’s saturation,`saturate()`is used.`100%`saturate represents the original element, while`0%`saturate denotes an element that is entirely unsaturated. An element that is super-saturated has a value greater than`100%`.

The result when adjusted to`100`saturate:

[](https://coderpad.io/wp-content/uploads/2022/09/image-17.png) The normal CoderPad logo, with 100% saturation, leading to a normal image

Other CSS filters include:

- invert(): The element is reversed by`invert()`. The default value of 0% represents the initial image. The image is entirely inverted when the percentage is`100%`. Negative values are not recognized by`invert`.
- opacity(): The image’s opacity effect is controlled by`opacity()`. When an element has`0%`opacity, it is fully transparent; when it has`100%`opacity, the original element is displayed. A negative value is not recognized by this filter.
- drop-shadow():`drop-shadow()`gives the element a drop shadow appearance. It accepts the values`blur`,`spread`,`color`,`h-shadow`, and`v-shadow`. It can be applied to shapes as well as images because it can have the same shape as the original. The shadow will be moved to the left of the image by the negative values for the`h-shadow`and`v-shadow`.
- url(): In order to apply some effects to an HTML element, the url filter uses a string value linking to an [SVG](https://www.adobe.com/creativecloud/file-types/image/vector/svg-file.html#:~:text=frequently%20asked%20questions-,What%20is%20an%20SVG%20file%3F,and%20lines%20on%20a%20grid.) filter element.

To learn more about CSS Filter properties with more in-depth practical examples, check out our article titled “ [Everything You Need to Know about All 11 CSS Filters](https://coderpad.io/blog/development/everything-you-need-to-know-about-all-11-css-filters/) ”.

## Set up a React project

The first thing we want to do in order to create our CSS-filter-powered photo editing application is to create a React project. We can do this by running the following command on our terminal:

```
npx create-react-app my-editor
```

This will automatically install all the dependencies needed to run a React app, and build a React project. To run your newly installed React app on your browser, run the`npm start`command. This is what it will look like in the browser:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7e875c8.png) The official starter template for React rendering. It shows the React logo spinning and “Edit src/App.js and save to reload.”

## Build out the HTML using a bit of JSX

Let’s get started on adding in photo editing functionality to our newly generated React app by writing out some [JSX](https://www.w3schools.com/react/react_jsx.asp) for our application. The first thing we want to do is head into our`App.js`file and edit the code to look like this:

```
import React from 'react';
import './App.css';
import Slider from './Slider'
function App() {
  return (
    <<strong>div</strong> className='container'>
      <<strong>div</strong> className='main-image' />
      <<strong>div</strong> className='sidebar'>
       
      </<strong>div</strong>>
      <<strong>Slider</strong> />
    </<strong>div</strong>>
  );
}
export default App;Code language: PHP (php)
```

Here, we have created a`div`for our`container`,`main-image`, and`sidebar`. We’ll style these classes in the next section.

After this, we created a`Slider.js`custom component and included the following code:

```
import React from 'react';
export default function Slider() {
    return (
        <<strong>div</strong> className='slider-container'>
            <<strong>input</strong> type='range' ClassName='slider' />
        </<strong>div</strong>>
    )
}Code language: PHP (php)
```

Within the`Slider`component we added an input to act as our slider. We did that by creating a`div`with a`className`of`slider-container`and, inside our`container`, added the expected`input`element. This is what it looks like when you run the code:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7eec0ce.png) A browser pointed at localhost:3000 showing a browser’s default range slider rendered

Now we have a slider for our project that we can use later.

## Style an app sidebar with CSS

Let’s do some styling in our`App.css`file. In our`App.css`file, we’ll include the following code:

```
*::before, *::after {
  box-sizing: border-box;
}
body {
  margin: 0;
}
.container {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: 1fr auto;
  grid-template-areas:
    "image sidebar"
    "slider sidebar";
  height: 100vh;
  width: 100vw;
  background-color: #DADADA;
}
.main-image {
  grid-area: image;
  background-image: url("/wp-content/themes/coderpad/assets/logos/coderpad.svg");
  width: 100%;
  height: 100%;
  background-position: top center;
  background-size: contain;
  background-repeat: no-repeat;
}
.sidebar {
  grid-area: sidebar;
  background-color: pink;
}
.slider-container {
  grid-area: slider;
  margin-top: 2rem;
  padding: 2rem;
}
.slider {
  width: 100%;
  cursor: pointer;
}Code language: CSS (css)
```

In this code snippet, we first set our`margin`to`0`, to remove any margin from our body. Then we styled the rest of our project to make sure our image takes as much space as possible while everything else is pushed to the side.

The image below shows the result of the CSS above:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7f12f6c.png) A pink sidebar rendered to the right-hand side of the page. In the center of the page is the CoderPad logo and on the bottom of the page is our input slider.

Sidebar

Before we style our sidebar, we need to actually put some content inside of it. To do that, we’ll first create a`SidebarItem`component in our`App.js`file:

```
function App() {
  return (
    <<strong>div</strong> className='container'>
      <<strong>div</strong> className='main-image' />
      <<strong>div</strong> className='sidebar'>
        <<strong>SidebarItem</strong> />
        <<strong>SidebarItem</strong> />
      </<strong>div</strong>>
      <<strong>Slider</strong> />
    </<strong>div</strong>>
  );
}
export default App;Code language: PHP (php)
```

Then we’ll create a`SidebarItem.js`component in our`src`folder, where we can include the following code:

```
import React from 'react'
export default function SidebarItem() {
    return(
        <<strong>button</strong> className='sidebar-item'>Sidebar Item</<strong>button</strong>>
    )
}Code language: JavaScript (javascript)
```

This is the result in the image below:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7f85d72.png) Two browser default styled buttons inside of the pink sidebar off to the right of the screen.

Sidebar CSS

OK! Now to style our sidebar. Let’s head into our`App.css`file again to make the following changes:

```
.sidebar {
  grid-area: sidebar;
  background-color: pink;
  border-left: 1px solid hsl(265, 100%, 56%);
  display: flex;
  flex-direction: column;
  align-items: stretch;
}
.sidebar-item {
  cursor: pointer;
  border: none;
  outline: none;
  background-color: pink;
  padding: 1rem;
  position: relative;
  transition: background-color 150ms;
}
.sidebar-item:hover, .sidebar-item:focus {
  background-color: hsl(265, 100%, 76%);
}
.sidebar-item::after {
  content: ' ';
  position: absolute;
  width: 80%;
  left: 10%;
  bottom: 0;
  height: 1px;
  background-color: hsl(265, 100%, 46%);
}
.sidebar-item:last-child::after {
  display: none;
}Code language: CSS (css)
```

Using the above CSS, we styled our sidebar to give us the result in the GIF below:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f7fcf309.gif) A right-handed sidebar with a darker background hover state and a light fade effect between hovering and not.

## Add in sidebar functionality with React

Now that we’ve gotten styling out of the way – let’s get going on the fun part: where we add in different controls for CSS Filter options. To get started, we’ll first write some code in our`App.js`file:

```
import React, {useState, useMemo} from 'react';
import './App.css';
import Slider from './Slider'
import SidebarItem from './SidebarItem'

const getDefaultOptions = () => [{
    name: 'Brightness',
    property: 'brightness',
    value: 100,
    range: {
      min: 0,
      max: 200
    },
    unit: '%'
  },
  {
    name: 'Contrast',
    property: 'contrast',
    value: 100,
    range: {
      min: 0,
      max: 200
    },
    unit: '%'
  },
  {
    name: 'Saturation',
    property: 'saturate',
    value: 100,
    range: {
      min: 0,
      max: 200
    },
    unit: '%'
  },
  {
    name: 'Grayscale',
    property: 'grayscale',
    value: 0,
    range: {
      min: 0,
      max: 100
    },
    unit: '%'
  },
  {
    name: 'Sepia',
    property: 'sepia',
    value: 0,
    range: {
      min: 0,
      max: 100
    },
    unit: '%'
  },
  {
    name: 'Hue Rotate',
    property: 'hue-rotate',
    value: 0,
    range: {
      min: 0,
      max: 360
    },
    unit: 'deg'
  },
  {
    name: 'Blur',
    property: 'blur',
    value: 0,
    range: {
      min: 0,
      max: 20
    },
    unit: 'px'
  }
];
function App() {
  const [selectedOptionIndex, setSelectedOptionIndex] = useState(0)
  const [options, setOptions] = useState(getDefaultOptions())
  const selectedOption = options[selectedOptionIndex]
  return (
    <div className='container'>
      <div className='main-image' />
      <div className='sidebar'>
        {options.map((option, index) => {
          return (<SidebarItem
            key={index}
            name={option.name}
            active={index === selectedOptionIndex}
            handleClick={() => setSelectedOptionIndex(index)}
          />
          )
        })}
      </div>
      <Slider />
    </div>
  );
}
export default App;Code language: JavaScript (javascript)
```

First, we create a variable`DEFAULT_OPTIONS`, then we set it to an array of objects. These objects define each one of our buttons, with each of them containing the names of the CSS filter we are using for the project. For each of our objects, there is a`property`field that is set to a CSS filter name, and this is what it corresponds to in CSS.

We also have our default`value`, the`range`which is essentially the`min`and`max`of our slider, then we have our`unit`for each CSS filter object.

Next, we’ll take our`const DEFAULT_OPTIONS`, and turn them into an`options`object we can use in React, and modify it. We’ll also edit our`sidebar.js`file to look like this:

```
import React from 'react'
export default function SidebarItem({ name, active, handleClick  }) {
    return(
        <<strong>button</strong>
        className={`sidebar-item ${active ? 'active' : ''}`}
        >
            { name }
            </<strong>button</strong>>
    )
}Code language: PHP (php)
```

Here, we’re taking the`name`from the props in our`App.js`file like this;`{ name }`. Below are the results of our code above:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f80332ed.gif) A list of CSS filters in the right-hand sidebar that includes the following filters: Brightness, Contrast, Saturation, Grayscale, Sepia, Hue Rotate, and Blur

## Make the CSS filter input slider functional

Now we’ll look at how to make our slider correspond with our active value. In our`App.js`file, we’ll include the following code:

```
<Slider
        min={selectedOption.range.min}
        max={selectedOption.range.max}
        value={selectedOption.value}
        handleChange={handleSliderChange}
      />Code language: HTML, XML (xml)
```

In our`Slider`, we passed in different props than we had previously, which in our case is the`min`,`max`,`value`, and`handleChange`. Then, inside of`App`, we created a function to handle changes in selected options for us:

```
function App() {
  const [selectedOptionIndex, setSelectedOptionIndex] = useState(0)
  const [options, setOptions] = useState(DEFAULT_OPTIONS)
  const selectedOption = options[selectedOptionIndex]

  function handleSliderChange({ target }) {
    setOptions(prevOptions => {
      return prevOptions.map((option, index) => {
        if (index !== selectedOptionIndex) return option
        return { ...option, value: target.value }
      })
    })
  }Code language: JavaScript (javascript)
```

Now that we have our function created, we are passing all the needed values down to our`Slider`. Now, in our`slider.js`file let’s use these new values. To do this, we’ll add the following code:

```
import React from 'react'
export default function Slider({ min, max, value, handleChange}) {
  return (
    <<strong>div</strong> className="slider-container">
      <<strong>input</strong>
        type="range"
        className="slider"
        min={min}
        max={max}
        value={value}
      />
    </<strong>div</strong>>
  )
}Code language: PHP (php)
```

We now have the results below:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f80dbcef.gif) A user clicking between different CSS filter options and changing the input slider associated with this CSS filter. These options do not yet impact the image in any way

As shown in the example above, when we change these different values, it updates the values of the filter selected internally.

While this is great progress, it’s not yet applying those CSS filters to the image itself. Let’s fix that.

## The final product: Apply CSS filters to an image

All that is left to do to make our image editor functional is take all the values and add them to our image and do the filtering for us:

```
<strong>const</strong> getImageStyle = useMemo (<strong>function</strong> <strong>getImageStyle</strong>() {
 
<strong>const</strong> filters = options.map(option => {
    <strong>return</strong> `${option.property}(${option.value}${option.unit})`
  }
)

  <strong>return</strong> { filter: filters.join(' ') }
}, [options])

console.log(getImageStyle)

  <strong>return</strong> (
    <div className='container'>
      <div className="main-image" style={getImageStyle} />
      <div className='sidebar'>
        {options.map((option, index) => {
          <strong>return</strong> (<SidebarItem
            key={index}
            name={option.name}
            active={index === selectedOptionIndex}
   
   
  handleClick={() => setSelectedOptionIndex(index)}
        />
        )
      })}Code language: HTML, XML (xml)
```

In our`App.js`file, we first create a`const getImageStyle = useMemo`, which is going to contain the style we apply to our image. In our function, we created our`const filters = options. map`. This map loops through all of our options to get them converted to a usable CSS property.

We are now done with our demo photo editor app with React and CSS Filters.

Take a look at the practical results.

Here’s us modifying Brightness, Contrast, and Saturation:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f830e214.gif) A user changing the brightness of an image all the way down and back up, then doing the same with contrast and saturation. The CSS filters work the same way a traditional photo editing app would.

Now here’s an example of us modifying the Grayscale, Sepia, Hue Rotate, and Blur:

[](https://coderpad.io/wp-content/uploads/2022/09/img_631f6f877de1c.gif) The user is modifying different color properties, such as grayscale, sepia, and hue rotate. Then, they’re adding a blur to the final result.

Try out the photo editor in the sandbox below:

## Wrapping up

Wow! We’ve learned quite a bit just by creating a simple photo editing application that uses CSS filters to apply different effects to an image.

If you want to see the full source code for the React application we built today, you can see the source code over [on my GitHub repository containing the project](https://github.com/uma-victor1/photo-editor).

Once you’ve mastered CSS filters, you’ll likely want to use them in combination with CSS math functions throughout your app’s styling. [Here’s an article outlining what CSS math functions are, and how to use them](https://coderpad.io/blog/development/an-overview-of-five-css-math-functions/).

Lastly, as you continue to expand your React photo editor, you’ll need a way to show your users different pages based on the URL. [Here’s how you can add in a routing solution into React](https://coderpad.io/blog/development/guide-to-react-router/).

Hopefully this article has been helpful in learning how to practically apply CSS filters and build an app of your own. Until next time!

Uma Victor is a software developer based in Nigeria who is familiar with a variety of different web technologies and frameworks. He is also keen on finding ways to explain things as simple as possible. You can reach out to me on [twitter](https://twitter.com/umavictor_).

## Subscribe for more CoderPad insights

## Trending Articles

Previous slide Next slide [Development

## What is Prompt Engineering?

Introducing AI tools such as ChatGPT and Bard helps increase the efficiency of tasks such as data generation, code generation and completion, text summarization, etc. How were these AI tools trained to answer almost every question you ask them? The answer, prompt engineering, is discussed in this blog post.

Read the post](https://coderpad.io/blog/development/what-is-prompt-engineering/) [Development

## A Guide To Using React’s useCallback() Hook

Are you looking to optimize your React applications? Have you heard of the useCallback hook? useCallback is a powerful React hook that helps you optimize your application by preventing unnecessary re-renders. In this post, we'll dive into React's useCallback() hook, define referential equality and callback functions and how to tie it all together.

Read the post](https://coderpad.io/blog/development/a-guide-to-using-reacts-usecallback-hook/) [Development

## MySQL vs. PostgreSQL: How Do They Compare?

A database is a convenient tool that allows users to access information, maintain records, and apply commonly used operations, such as insertion, modification, deletion, and data organization. Learn about MySQL vs PostgreSQL, their features, advantages, and disadvantages. Finally, check out which one is the best choice for you.

Read the post](https://coderpad.io/blog/development/mysql-vs-postgresql-how-do-they-compare/) [Development

## PostgreSQL LIKE Operator: A Detailed Guide

When using databases, searching for values that match a given pattern is a familiar necessity. Learn how you can retrieve entries with queries including a particular string using Postgres LIKE operator.

Read the post](https://coderpad.io/blog/development/postgresql-like-operator-a-detailed-guide/) [Development

## How To Use DataProvider In TestNG With Examples

When it comes to testing there are many tools and techniques available. In this post, we'll discuss how to use DataProvider with TestNG.

Read the post](https://coderpad.io/blog/development/how-to-use-dataprovider-in-testng-with-examples/) [Development

## How To Get Up And Running With Django Migrations: A Guide

Django migrations are a way of handling the application of changes to a database schema. By following the steps outlined in this guide, you can easily set up and use migrations in your Django projects.

Read the post](https://coderpad.io/blog/development/how-to-get-up-and-running-with-django-migrations-a-guide/) [Development

## Writing A Parameterized Test In JUnit With Examples

A JUnit parameterized test allows you to decouple data from structure, generating cleaner and more reliable tests. Learn more in this guide.

Read the post](https://coderpad.io/blog/development/writing-a-parameterized-test-in-junit-with-examples/) [Development

## How To Use Assert In JUnit With Examples

The JUnit Assert class is what you use to write unit testing assertions in JUnit. Learn more about it in this guide, full of examples.

Read the post](https://coderpad.io/blog/development/how-to-use-assert-in-junit-with-examples/) [Development

## A Simple Guide To TypeORM: What It Is And How To Use It

Considering current languages have object-oriented programming features, most database management systems are based on the relational model, with tables instead of objects. For these reasons, mapping relational objects is very important in development. In this article, you will learn what TypeORM, one of the main tools used today, is.

Read the post](https://coderpad.io/blog/development/a-simple-guide-to-typeorm-what-it-is-and-how-to-use-it/) [Development

## Using PreparedStatement in JDBC With Examples

In this post, we'll see why and how to work with JDBC and PreparedStatement. We will also learn along with examples using code.

Read the post](https://coderpad.io/blog/development/using-preparedstatement-in-jdbc-with-examples/) [Development

## React vs React Native: The Differences Explained

React and React Native are similar in that they both allow web developers to build applications based on the unique concept of components. But the two frameworks do this in radically different ways. In this post, we cover the key differences between react and react native frameworks, implementation, and which is an appropriate choice.

Read the post](https://coderpad.io/blog/development/react-vs-react-native-the-differences-explained/) [Development

## Logging in Django

The most challenging part of fixing a problem is identifying it. Unfortunately, this statement is exceptionally true when it comes to debugging. Learn how to use the power of logging into your Django applications to improve your debug process in this article.

Read the post](https://coderpad.io/blog/development/logging-in-django/)

## Hire with confidence.

See why engineering teams trust CoderPad to evaluate developers with realistic, AI-ready hiring workflows.

[Request a demo](https://coderpad.io/sales/)
