Image Compressor using ReactJS (original) (raw)

Last Updated : 25 Jul, 2024

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:

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

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; } }

JavaScript

//App.js import React from 'react'; import './App.css'; import CompressorComp from "./Components/Compressor"; import 'bootstrap/dist/css/bootstrap.css'; function App() { return ( ); } 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;

`

**Steps to run the application:

npm start

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