Open In App

Image Compressor using ReactJS

Last Updated : 29 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

This article focuses on crafting an Interactive Feature-based Image Compressor utilizing the ReactJS library. Users can upload image files and adjust compression quality via a slider. Upon setting the compression quality and initiating compression, users can download the compressed image locally. Additionally, the application offers navigation for accessing Instructions and Compression History.

Output Preview: Let us have a look at how the final output will look like.

Screenshot-2023-11-05-at-10-31-29-Image-Compressor

Prerequisites

Steps to create the React App:

Step 1: Create a React App

npx create-react-app image-compressor

Step 2: Navigate to the newly created project folder by executing the below command.

cd image-compressor

Step 3: Steps to install required modules

npm install react-bootstrap @fortawesome/react-fontawesome @fortawesome/free-solid-svg-icons 
npm install image-conversion bootstrap

Project Structure:

Screenshot-2024-01-27-221804

The updated dependencies in package.json will look like this:

"dependencies": {
"@fortawesome/free-solid-svg-icons": "^6.4.2",
"@fortawesome/react-fontawesome": "^0.2.0",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^5.3.2",
"image-conversion": "^2.1.1",
"react": "^18.2.0",
"react-bootstrap": "^2.9.1",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
}

Approach to create Image Compressor:

  • The application is structured using React components like Buttons, NavBar, Modal, Spinner, etc., along with CSS for styling.
  • Image upload triggers validation to ensure only image files can be compressed, with customization options for compression quality.
  • Users can download compressed images and access features like viewing instructions and past compression history.
  • The interface is organized with sections for navigation, image display/upload, compression controls, and a Reset Button for clearing uploaded files.

Example: Insert the below code in the App.js, Compressor.js, and Compressor.css file mentioned in the above directory structure.

Javascript




//App.js
import React from 'react';
import './App.css';
import CompressorComp
    from "./Components/Compressor";
import 'bootstrap/dist/css/bootstrap.css';
function App() {
    return (
        <CompressorComp />
    );
}
export default App;


Javascript




//Components/Compressor.js
import React,
{
    useState, useEffect
} from 'react';
import {
    Navbar, Card,
    Spinner, Modal,
    Button
} from 'react-bootstrap';
import { FontAwesomeIcon }
    from '@fortawesome/react-fontawesome';
import {
    faImage, faDownload,
    faUpload, faImage as faImagePlaceholder,
    faQuestionCircle,
    faHistory
} from '@fortawesome/free-solid-svg-icons';
import './Compressor.css';
import { compress }
    from 'image-conversion';
function CompressorComp() {
    const [compressedLink, setCompressedLink] = useState('');
    const [originalImage, setOriginalImage] = useState(null);
    const [originalLink, setOriginalLink] = useState('');
    const [uploadImage, setUploadImage] = useState(false);
    const [outputFileName, setOutputFileName] = useState('');
    const [compressionQuality, setCompressionQuality] = useState(0.8);
    const [originalSize, setOriginalSize] = useState(0);
    const [compressedSize, setCompressedSize] = useState(0);
    const [isCompressed, setIsCompressed] = useState(false);
    const [compressionInProgress, setCompressionInProgress] = useState(false);
    const [loading, setLoading] = useState(false);
    const [showHelp, setShowHelp] = useState(false);
    const [showHistory, setShowHistory] = useState(false);
    const [compressedHistory, setCompressedHistory] = useState([]);
    const [showCompressedImage, setShowCompressedImage] = useState(false);
    const [modalShow, setModalShow] = useState(false);
 
    useEffect(() => {
        if (originalImage) {
            setCompressedLink('');
            setCompressedSize(0);
            setIsCompressed(false);
            setShowCompressedImage(false);
        }
    }, [originalImage]);
 
    async function uploadLink(event) {
        const imageFile = event.target.files[0];
        setOriginalLink(URL.createObjectURL(imageFile));
        setOriginalImage(imageFile);
        setOutputFileName(imageFile.name);
        setUploadImage(true);
        setOriginalSize(imageFile.size);
    }
    async function compressImage() {
        if (!originalImage) {
            alert('Please upload an image first.');
            return;
        }
        try {
            setCompressionInProgress(true);
            setShowCompressedImage(false);
            setLoading(true);
            const compressedImage =
                await compress(originalImage, {
                    quality: compressionQuality,
                    width: 800,
                    height: 800,
                });
            setCompressedLink(URL.createObjectURL(compressedImage));
            setCompressedSize(compressedImage.size);
            setIsCompressed(true);
            setCompressedHistory(
                [
                    ...compressedHistory,
                    {
                        link: compressedLink,
                        name: outputFileName
                    }
                ]);
            setTimeout(
                () => {
                    setLoading(false);
                    setShowCompressedImage(true);
                }, 2000);
        } catch (error) {
            console.error('Image compression failed:', error);
            alert('Image compression failed. Please try again.');
        } finally {
            setCompressionInProgress(false);
        }
    }
    function resetApp() {
        setOriginalLink('');
        setOriginalImage(null);
        setUploadImage(false);
        setOutputFileName('');
        setCompressionQuality(0.8);
        setOriginalSize(0);
        setCompressedSize(0);
        setIsCompressed(false);
        setCompressedLink('');
        setShowCompressedImage(false);
    }
    function toggleHelp() {
        setShowHelp(!showHelp);
    }
    function toggleHistory() {
        setShowHistory(!showHistory);
    }
    return (
        <div className="mainContainer">
            <Navbar className="navbar justify-content-between"
                bg="lig" variant="dark">
                <div>
                    <Navbar.Brand className="navbar-content">
                        <center>
                            <FontAwesomeIcon icon={faImage}
                                className="icon" />
                            GeeksforGeeks Image Compressor
                        </center>
                    </Navbar.Brand>
                </div>
                <div className="navbar-actions">
                    <FontAwesomeIcon icon={faQuestionCircle}
                        className="help-icon" onClick={toggleHelp} />
                    <FontAwesomeIcon icon={faHistory}
                        className="history-icon" onClick={toggleHistory} />
                </div>
            </Navbar>
            {showHelp && (
                <div className="help-container">
                    <p>Instructions:</p>
                    <ul>
                        <li>
                            Upload an image using
                            the "Upload a file" button.
                        </li>
                        <li>
                            Adjust the compression
                            quality using the slider.
                        </li>
                        <li>
                            Press the "Compress" button
                            to start the compression.
                        </li>
                        <li>
                            Download the compressed image
                            using the "Download" button.
                        </li>
                    </ul>
                </div>
            )}
            {showHistory && (
                <div className="history-container">
                    <p>Compressed History:</p>
                    <ul>
                        {
                            compressedHistory.map(
                                (item, index) => (
                                    <li key={index}>
                                        <a href={item.link}
                                            download={item.name}>
                                            {item.name}
                                        </a>
                                    </li>
                                ))
                        }
                    </ul>
                </div>
            )}
            <div className="row mt-5">
                <div className="col-xl-3 col-lg-3
                col-md-12 col-sm-12">
                    {uploadImage ? (
                        <Card.Img className="image"
                            variant="top" src={originalLink}
                            alt="Original Image" />
                    ) : (
                        <Card.Img className="uploadCard"
                            variant="top" src={faUpload} alt="" />
                    )}
                    <div className="d-flex justify-content-center
                    upload-btn-wrapper">
                        <label htmlFor="uploadBtn"
                            className="btn btn-primary">
                            <FontAwesomeIcon icon={faUpload}
                                className="icon" />
                            Upload a file
                        </label>
                        <input
                            type="file"
                            id="uploadBtn"
                            accept="image/*"
                            className="mt-2 btn btn-primary w-75"
                            onChange={(event) => uploadLink(event)} />
                    </div>
                </div>
                <div
                    className="col-xl-6 col-lg-6
                        col-md-12 col-sm-12
                        d-flex justify-content-center
                        align-items-baseline">
                    <div>
                        {outputFileName ? (
                            <div>
                                <label htmlFor="qualitySlider">
                                    Compression Quality:
                                </label>
                                <input
                                    id="qualitySlider"
                                    type="range"
                                    min="0.1"
                                    max="1"
                                    step="0.1"
                                    value={compressionQuality}
                                    onChange={
                                        (event) =>
                                            setCompressionQuality(
                                                parseFloat(event.target.value)
                                            )
                                    }
                                />
                                <div className="text-center">
                                    Original Size:
                                    {
                                        Math.round(originalSize / 1024)
                                    } KB
                                    <br />
                                    Compressed Size:
                                    {
                                        Math.round(compressedSize / 1024)
                                    } KB
                                </div>
                                <div className="text-center">
                                    {isCompressed &&
                                        !compressionInProgress && (
                                            <div className="text-success
                                            compressed-message">
                                                Image compressed successfully!
                                            </div>
                                        )}
                                    {
                                        compressionInProgress &&
                                        <div className="text-info">
                                            Compressing image...
                                        </div>
                                    }
                                </div>
                                <div className="button-container">
                                    {loading ? (
                                        <div className="text-info">
                                            Loading...
                                        </div>
                                    ) : (
                                        <button type="button"
                                            className="btn btn-success"
                                            onClick={compressImage}>
                                            <FontAwesomeIcon icon={faImage}
                                                className="icon" />
                                            Compress
                                        </button>
                                    )}
                                    <button type="button"
                                        className="btn btn-danger ml-3"
                                        onClick={resetApp}>
                                        Reset
                                    </button>
                                </div>
                            </div>
                        ) : (
                            <></>
                        )}
                    </div>
                </div>
                <div className="col-xl-3 col-lg-3 col-md-12 col-sm-12">
                    {showCompressedImage ? (
                        <div>
                            <Card.Img
                                className="image"
                                variant="top"
                                src={compressedLink}
                                alt="Compressed Image"
                                onClick={() => setModalShow(true)}
                                style={{ cursor: 'pointer' }}
                            />
                            <a href={compressedLink}
                                download={outputFileName}
                                className="mt-2 btn btn-success
                                w-75 download-btn">
                                <FontAwesomeIcon icon={faDownload}
                                    className="icon" />
                                Download
                            </a>
                            <Modal show={modalShow}
                                onHide={
                                    () =>
                                        setModalShow(false)
                                } size="lg">
                                <Modal.Body className="text-center">
                                    <Card.Img className="image"
                                        variant="top" src={compressedLink}
                                        alt="Compressed Image" />
                                </Modal.Body>
                                <Modal.Footer>
                                    <Button variant="secondary"
                                        onClick={
                                            () => setModalShow(false)
                                        }>
                                        Close
                                    </Button>
                                </Modal.Footer>
                            </Modal>
                        </div>
                    ) : (
                        <div className="d-flex align-items-center
                        justify-content-center">
                            {
                                compressionInProgress &&
                                <Spinner animation="border" variant="primary" />
                            }
                            {
                                !uploadImage &&
                                !compressionInProgress && (
                                    <FontAwesomeIcon icon={faImagePlaceholder}
                                        className="icon" size="3x" />
                                )
                            }
                        </div>
                    )}
                </div>
            </div>
        </div>
    );
}
export default CompressorComp;


CSS




/* Components/Compressor.css */
.center {
    text-align: center !important;
  }
  .mainContainer {
    margin: 0;
    text-align: center;
  }
  @media (max-width: 768px) {
    .mainContainer {
      margin: 0;
    }
}
  .navbar {
    z-index: 1041;
    box-shadow: 0 4px 8px rgba(233, 12, 12, 0.2);
    background-color: #fff78b !important;
    padding: 20px;
  }
  .navbar-content {
    color: green !important;
    text-align: center !important;
    font-weight: bold;
    font-size: 24px;
    text-transform: uppercase;
    margin: 0;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100%;
  }
  .help-icon,
  .history-icon {
    font-size: 24px;
    cursor: pointer;
    margin-right: 20px;
    color: #000;
  }
  .help-icon:hover,
  .history-icon:hover {
    color: #3498db;
  }
  .help-container,
  .history-container {
    text-align: left;
    padding: 10px;
    background-color: #f5f5f5;
    border: 1px solid #ccc;
    border-radius: 5px;
    margin: 10px;
    max-height: 200px;
    overflow-y: auto;
  }
  .social-icons {
    margin-right: 10px;
    box-sizing: border-box;
    width: 1.5em !important;
    height: 1.5em !important;
    color: #ecf0f1;
    transition: color 0.3s;
  }
  .social-icons:hover {
    color: #e74c3c;
  }
  .uploadCard {
    width: 80%;
    display: inline-block;
  }
  .image {
    display: block;
    max-width: 100%;
    height: auto;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
    border: 1px solid #ecf0f1;
  }
  .upload-btn-wrapper {
    position: relative;
    overflow: hidden;
    display: inline-block;
  }
  .btn {
    border: none;
    color: white;
    padding: 10px 20px;
    border-radius: 8px;
    font-size: 20px;
    font-weight: bold;
    cursor: pointer;
    transition: background-color 0.3s, transform 0.2s;
  }
   
  
  .btn:hover {
    background-color: #2980b9;
    transform: scale(1.05);
  }
  .upload-btn-wrapper input[type="file"] {
    font-size: 100px;
    position: absolute;
    left: 0;
    top: 0;
    opacity: 0;
  }
  #qualitySlider {
    width: 100%;
    padding: 0;
    margin: 10px 0;
  }
  .btn.download-btn {
    background-color: #2ecc71;
    transition: background-color 0.3s;
  }
  .btn.download-btn:hover {
    background-color: #27ae60;
  }
  .btn-reset {
    border: none;
    color: white;
    background-color: #e74c3c;
    padding: 10px 20px;
    border-radius: 8px;
    font-size: 20px;
    font-weight: bold;
    cursor: pointer;
    transition: background-color 0.3s, transform 0.2s;
    margin-left: 20px;
  }
  .btn-reset:hover {
    background-color: #c0392b;
    transform: scale(1.05);
  }
  .compressed-message {
    font-size: 24px;
    font-weight: bold;
    color: #2ecc71;
    margin-top: 10px;
    transition: color 0.3s;
  }
  .compressed-message:hover {
    color: #27ae60;
  }
  .button-container {
    display: flex;
    align-items: center;
    justify-content: center;
    margin-top: 20px;
  }
  @media (max-width: 768px) {
    .help-container,
    .history-container {
      width: 100%;
      max-width: none;
    }
  }


Steps to run the application:

npm start

Output: Type the following URL in the address bar http://localhost:3000/



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads