Build an Image Cropper using React JS & TailwindCSS
- React JS
- TailwindCSS
- Framer Motion
- React Easy Crop
- React Icons
- Drag And Drop File
- Crop Image
- Download Image
To get started follow these steps:
Using CLI:
git clone https://github.com/xdcode2/image-cropper.git
**Ensure you have installed Git on your machine.
Using GitHub:
- Go to the project repository on my GitHub page
- Click on the green button on the top 👆
- Click Download ZIP
Install the project dependencies using npm:
npm install
**Ensure you have installed NodeJS on your machine.
npm run dev
**This project uses Vite.
useFileReader.jsx
:
import { useEffect, useState } from "react";
const bytesToMB = (bytes) => {
if (bytes === 0) return 0;
return Math.round((bytes / (1024 * 1024)) * 100) / 100;
};
export const useFileReader = (options) => {
const { method, accept, maxFileSize } = options;
const [file, setFile] = useState(null);
const [error, setError] = useState({ type: null, msg: null });
const [result, setResult] = useState(null);
const [progress, setProgress] = useState(0);
useEffect(() => {
if (!file) return;
const fileType = file.name.split(".").pop().toLowerCase(),
fileSizeMB = bytesToMB(file.size);
if (accept && !accept.includes(fileType)) {
setError({ type: "fileType", msg: `Invalid file type: ${fileType}. Accepted types: ${accept.join(", ")}` });
}
if (maxFileSize && fileSizeMB > maxFileSize) {
setError({ type: "fileSize", msg: `File size exceeds the maximum limit of ${maxFileSize} MB.` });
}
const fr = new FileReader();
fr.onprogress = (event) => {
if (event.lengthComputable) {
const percentLoaded = (event.loaded / event.total) * 100;
setProgress(percentLoaded);
}
};
fr.onload = () => {
setResult(fr.result);
setProgress(100);
};
fr.onerror = (err) => {
setError({ type: "fileRead", msg: `Error reading file: ${file.name}. ${err.message}` });
};
fr[method](file);
return () => {
setError({ type: null, msg: null });
setResult(null);
setProgress(0);
};
}, [file]);
return [{ result, error, file, progress }, setFile];
};
tailwindcss.config.js
:
/** @type {import('tailwindcss').Config} */
export default {
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
theme: {
fontFamily: {
poppins: ["Poppins", "sans-serif"],
},
extend: {
colors: {
primary: {
10: "#b6cef0",
20: "#94b7e9",
30: "#72a0e3",
40: "#5089dc",
50: "#2e72d5",
60: "#2760b4",
70: "#1f4e92",
80: "#183c71",
90: "#102a4f",
},
white: {
50: "#ebeff3",
100: "#ffffff",
},
gray: "#a9aeba",
},
},
},
important: true,
plugins: [],
};
index.css
:
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600&display=swap");
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn-primary-fill {
@apply h-10 px-4 bg-primary-50 rounded-lg text-white-100 hover:bg-primary-60 transition-colors duration-300;
}
.btn-primary-outlined {
@apply h-10 px-4 border border-solid border-primary-50 rounded-lg text-primary-50 hover:border-primary-60 hover:text-primary-60 transition-colors duration-300;
}
}
.eslintrc.cjs
:
module.exports = {
root: true,
env: { browser: true, es2020: true },
extends: ["plugin:react/jsx-runtime", "plugin:react-hooks/recommended"],
ignorePatterns: ["dist", ".eslintrc.cjs"],
parserOptions: { ecmaVersion: "latest", sourceType: "module" },
settings: { react: { version: "18.2" } },
plugins: ["react-refresh"],
rules: {
"react-refresh/only-export-components": ["warn", { allowConstantExport: true }],
},
};
Hey bro please don't forget to subscribe to my channel 😊😊.