If you’ve ever found yourself spending way too much time picking the perfect color combination for a design, this guide is for you. We’re going to build something fun, practical, and visually stunning. By the end of this step-by-step guide, you’ll be able to create a Random Gradient Generator using React.js, which will allow you to generate endless color gradients (linear, radial, and conic) with just one click, copy their CSS code, and use them to make your projects stand out.
This guide is beginner-friendly and explains every line of code so you can learn React while building something fun and practical.
🚀 What We’re Building
- We’ll create a UI where users can:
- Choose how many gradients to generate
- Select the type of gradient (linear, radial, or conic)
- Click a button to generate random gradients
- Copy the CSS code for each gradient
- Load more gradients when available
Here’s a sneak peek of how it will look
📂 Project Setup
First, make sure you have a React project ready. Then, create two files inside the src/components
folder:
- GradientGenerator.jsx
- GradientGenerator.css
📝 Writing the Component
Let’s break the GradientGenerator.jsx step by step.
import { useEffect, useState } from "react";
import './GradientGenerator.css';
📝 We import React hooks (useState and useEffect) and the CSS file for styling.
1. Setting Up State
const [num, setNum] = useState(12);
const [type, setType] = useState('linear');
const [gradientCollections, setGradientCollections] = useState([]);
const [displayCount, setDisplayCount] = useState(12);
-
num
→ number of gradients to generate. -
type
→ type of gradient (linear
,radial
,conic
). -
gradientCollections
→ stores all generated gradients. -
displayCount
→ how many gradients to show at once.
We also calculate what to display:
const displayedGradients = gradientCollections.slice(0, displayCount);
const showLoadMore = displayCount < gradientCollections.length;
📝 👉 This helps us show only a part of
gradients
and reveal more with a “Load More Gradients” button.
2. Random Color Generator
const getHexColor = () => {
const rgb = 255 * 255 * 255;
const random = Math.floor(Math.random() * rgb);
const hexValue = random.toString(16);
const colorCode = hexValue.padEnd(6, '0');
return `#${colorCode}`;
};
📝 This function creates a random HEX color code every time it’s called.
3. Generating Gradients
const generateGradients = () => {
let colors = [];
for (let i = 0; i < num; i++) {
const degree = Math.floor(Math.random() * 360);
const color1 = getHexColor();
const color2 = getHexColor();
if (type === 'linear') {
colors.push({
gradient: `linear-gradient(${degree}deg, ${color1}, ${color2})`,
css: `background: linear-gradient(${degree}deg, ${color1}, ${color2})`
});
} else if (type === 'radial') {
colors.push({
gradient: `radial-gradient(circle, ${color1}, ${color2})`,
css: `background: radial-gradient(circle, ${color1}, ${color2})`
});
} else {
colors.push({
gradient: `conic-gradient(from ${degree}deg, ${color1}, ${color2})`,
css: `background: conic-gradient(from ${degree}deg, ${color1}, ${color2})`
});
}
}
setGradientCollections(colors);
setDisplayCount(Math.min(12, num));
};
📝 Depending on the selected gradient type, we push a new gradient style into the colors array.
4. Load More Functionality
const loadMoreGradients = () => {
const newCount = Math.min(displayCount + 12, gradientCollections.length);
setDisplayCount(newCount);
}
📝 Each time we click the “Load More Gradients” button, it shows 12 more gradients.
5. Copying CSS to Clipboard
const copyCss = (css) => {
navigator.clipboard.writeText(css);
const toast = document.createElement('div');
toast.textContent = 'Code Copied Successfully!';
toast.className = 'toast-notification';
document.body.appendChild(toast);
setTimeout(() => {
if (document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 3000);
};
📝 This allows users to copy the gradient CSS with a single click, and a toast message confirms success. In this section, we're creating our own custom toast notification (without using any external library).
6. Auto-Generate Gradients on Page Load
useEffect(() => {
generateGradients();
}, [type]);
📝 Every time the component first mounts or when the gradient type changes (linear, radial, or conic), the useEffect hook runs and triggers generateGradients(). This ensures fresh gradients are automatically generated without needing to click the button.
7. Rendering the UI
Now, let’s render the entire UI.
return (
<section className="gradient-generator-container">
<div className="background-elements">
<div className="floating-element element-1"></div>
<div className="floating-element element-2"></div>
<div className="floating-element element-3"></div>
</div>
<div className="content-wrapper">
<header className="gradient-header">
<div className="logo-container">
<div className="logo-icon">🎨</div>
</div>
<h1 className="main-title">Random Gradient Generator</h1>
<p className="subtitle">
Create stunning gradients for your next project with
just one click
</p>
</header>
<div className="controls">
<div className="control-group">
<input
type="number"
value={num}
onChange={(e) => setNum(e.target.value)}
className="number-input" />
</div>
<div className="control-group">
<select
value={type}
onChange={(e) => setType(e.target.value)}
className="select-input">
<option value="linear">Linear</option>
<option value="radial">Radial</option>
<option value="conic">Conic</option>
</select>
</div>
<button
onClick={generateGradients}
className="generate-button">
✨ Generate
</button>
</div>
<div className="gradient-grid">
{displayedGradients.map((color, index) => (
<div
key={index}
className="gradient-card"
style={{ background: color.gradient, animationDelay:
`${index * 0.01}s` }}>
<div className="overlay"></div>
<div className="gradient-info">
<div className="gradient-type">{type}</div>
</div>
<button onClick={() => copyCss(color.css)}
className="copy-button">
📋 Copy
</button>
</div>
))}
</div>
{showLoadMore && (
<div className="load-more-container">
<button
onClick={loadMoreGradients}
className="load-more-button">
Load More Gradients
</button>
</div>
)}
<div className="stats-container">
<div className="stats-badge">
<span className="stats-text">
Showing
<span className="stats-number">{displayCount}</span>
of
<span className="stats-number">
{gradientCollections.length}
</span> gradients
</span>
</div>
</div>
</div>
</section>
);
export default GradientGenerator;
📝 Here we:
- Render the header
- Add input controls
- Display generated gradients in a grid
- Add “Load More” and statistics section
- Finally, export it
Complete code snippet
import { useEffect, useState } from "react";
import './GradientGenerator.css';
const GradientGenerator = () => {
const [num, setNum] = useState(12);
const [type, setType] = useState('linear');
const [gradientCollections, setGradientCollections] = useState([]);
const [displayCount, setDisplayCount] = useState(12);
const displayedGradients = gradientCollections.slice(0, displayCount);
const showLoadMore = displayCount < gradientCollections.length;
const getHexColor = () => {
const rgb = 255 * 255 * 255;
const random = Math.floor(Math.random() * rgb);
const hexValue = random.toString(16);
const colorCode = hexValue.padEnd(6, '0');
return `#${colorCode}`;
};
const generateGradients = () => {
let colors = [];
for(let i = 0; i < num; i++) {
const degree = Math.floor(Math.random() * 360);
const color1 = getHexColor();
const color2 = getHexColor();
if(type === 'linear') {
colors.push({
gradient: `linear-gradient(${degree}deg, ${color1}, ${color2})`,
css: `background: linear-gradient(${degree}deg, ${color1}, ${color2})`
});
} else if(type === 'radial') {
colors.push({
gradient: `radial-gradient(circle, ${color1}, ${color2})`,
css: `background: radial-gradient(circle, ${color1}, ${color2})`
});
} else {
colors.push({
gradient: `conic-gradient(from ${degree}deg, ${color1}, ${color2})`,
css: `background: conic-gradient(from ${degree}deg, ${color1}, ${color2})`
})
}
}
setGradientCollections(colors);
setDisplayCount(Math.min(12, num));
};
const loadMoreGradients = () => {
const newCount = Math.min(displayCount + 12, gradientCollections.length);
setDisplayCount(newCount);
}
const copyCss = (css) => {
navigator.clipboard.writeText(css);
const toast = document.createElement('div');
toast.textContent = 'Code Copied Successfully!';
toast.className = 'toast-notification';
document.body.appendChild(toast);
setTimeout(() => {
if(document.body.contains(toast)) {
document.body.removeChild(toast);
}
}, 3000);
}
useEffect(() => {
generateGradients();
}, [type]);
return (
<section className="gradient-generator-container">
<div className="background-elements">
<div className="floating-element element-1"></div>
<div className="floating-element element-2"></div>
<div className="floating-element element-3"></div>
</div>
<div className="content-wrapper">
<header className="gradient-header">
<div className="logo-container">
<div className="logo-icon">🎨</div>
</div>
<h1 className="main-title">Random Gradient Generator</h1>
<p className="subtitle">Create stunning gradients for your next project with just one click</p>
</header>
<div className="controls">
<div className="control-group">
<input type="number" value={num} onChange={(e) => setNum(e.target.value)} className="number-input"/>
</div>
<div className="control-group">
<select value={type} onChange={(e) => setType(e.target.value)} className="select-input">
<option value="linear">Linear</option>
<option value="radial">Radial</option>
<option value="Conic">Conic</option>
</select>
</div>
<button onClick={generateGradients} className="generate-button">✨ Generate</button>
</div>
<div className="gradient-grid">
{ displayedGradients.map((color, index) => (
<div key={index} className="gradient-card" style={{ background: color.gradient, animationDelay: `${index * 0.01}s`}}>
<div className="overlay"></div>
<div className="gradient-info">
<div className="gradient-type">{type}</div>
</div>
<button onClick={() => copyCss(color.css)} className="copy-button">📋 Copy</button>
</div>
))}
</div>
{ showLoadMore && (
<div className="load-more-container">
<button onClick={loadMoreGradients} className="load-more-button">Load More Gradients</button>
</div>
)}
<div className="stats-container">
<div className="stats-badege">
<span className="stats-text">
Showing
<span className="stats-number">{displayCount}</span> of {' '}
<span className="stats-number">{gradientCollections.length}</span> gradient colors
</span>
</div>
</div>
</div>
</section>
)
}
export default GradientGenerator;
8. Styling with CSS 🎨
All the animations, hover effects, and responsive design come from the GradientGenerator.css file. It includes:
Background animations (floating cir
- cles, glowing effects)
- Grid layout for gradients
- Hover states with smooth animations
- A toast notification for copied code
📝 You can find the full CSS in the code here.
.gradient-generator-container {
min-height: 100vh;
background: linear-gradient(135deg, hsl(240, 10%, 3.9%) 0%, hsl(240, 10%, 3.9%) 50%, hsl(240, 10%, 6%) 100%);
padding: 1rem;
position: relative;
overflow-x: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
color: hsl(0, 0%, 98%);
}
@media (min-width: 640px) {
.gradient-generator-container {
padding: 2rem;
}
}
.background-elements {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
overflow: hidden;
z-index: 0;
}
.floating-element {
position: absolute;
width: 18rem;
height: 18rem;
border-radius: 50%;
mix-blend-mode: multiply;
filter: blur(40px);
opacity: 0.2;
animation: float 3s ease-in-out infinite;
}
.element-1 {
top: 5rem;
left: 2.5rem;
background: linear-gradient(135deg, hsl(270, 80%, 60%), hsl(300, 80%, 65%));
animation-delay: 0s;
}
.element-2 {
top: 10rem;
right: 2.5rem;
background: linear-gradient(135deg, hsl(195, 100%, 60%), hsl(270, 80%, 60%));
animation-delay: 2s;
}
.element-3 {
bottom: -2rem;
left: 5rem;
background: linear-gradient(135deg, hsl(240, 80%, 55%), hsl(270, 80%, 60%));
animation-delay: 4s;
}
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-2.5rem);
}
}
.content-wrapper {
position: relative;
max-width: 112rem;
margin: 0 auto;
z-index: 1;
}
.gradient-generator-container {
text-align: center;
animation: slide-up 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
.logo-container {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem;
background: linear-gradient(135deg, hsl(270, 80%, 60%), hsl(300, 80%, 65%));
border-radius: 1rem;
margin-bottom: 1.5rem;
box-shadow: 0 0 40px hsl(270, 80%, 60% / 0.3);
animation: glow 2s ease-in-out infinite alternate;
}
.logo-icon {
font-size: 2.5rem;
}
.main-title {
font-size: 2.5rem;
font-weight: 700;
background: linear-gradient(135deg, hsl(270, 80%, 60%), hsl(195, 100%, 60%), hsl(270, 80%, 60%));
background-size: 300%;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
margin-bottom: 1rem;
line-height: 1.2;
animation: gradient-shift 8s ease-in-out infinite;
}
@media (min-width: 640px) {
.main-title {
font-size: 3.75rem;
}
}
.subtitle {
font-size: 1.25rem;
color: hsl(240, 5%, 65%);
max-width: 100%;
margin: 0 auto;
margin-bottom: 2rem;
}
.controls {
display: flex;
justify-content: center;
margin-bottom: 3rem;
animation: fade-in 0.6s cubic-bezier(0.23, 1, 0.32, 1);
animation-delay: 0.2s;
animation-fill-mode: both;
gap: 0.75rem;
}
.control-group {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.75rem;
}
.input-wrapper,
.select-wrapper {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
}
.number-input,
.select-input {
padding: 0.8rem 1rem;
border-radius: 0.5rem;
border: 2px solid hsl(240, 10%, 15% / 0.5);
background: hsl(240, 10%, 6% / 0.8);
backdrop-filter: blur(12px);
color: hsl(262, 93%, 12%);
font-size: 1rem;
font-weight: 500;
outline: none;
transition: all 0.3s cubic-bezier(0.23, 1, 0.32, 1);
text-align: center;
min-height: 2.5rem;
}
.number-input {
width: 5rem;
}
.select-input {
width: 10rem;
appearance: none;
cursor: pointer;
padding-right: 2.5rem;
}
.number-input:focus,
.select-input:focus {
border-color: hsl(270, 80%, 60%);
box-shadow: 0 0 0 4px hsl(270, 80%, 60% / 0.2);
background: hsl(240, 10%, 8% / 0.9);
}
.generate-button {
position: relative;
display: flex;
align-items: center;
justify-content: center;
gap: 0.75rem;
padding: 0.5rem 1.5rem;
border-radius: 0.5rem;
border: none;
background: linear-gradient(135deg, hsl(270, 80%, 60%), hsl(300, 80%, 65%));
color: hsl(0, 0%, 98%);
font-size: 1.1rem;
font-weight: 700;
cursor: pointer;
transition: all 0.4s cubic-bezier(0.23, 1, 0.32, 1);
box-shadow:
0 8px 25px -8px hsl(270, 80%, 60% / 0.4),
0 0 0 1px hsl(270, 80%, 60% / 0.1);
overflow: hidden;
}
.generate-button:hover {
transform: translateY(-2px) scale(1.02);
box-shadow:
0 12px 35px -8px hsl(270, 80%, 60% / 0.5),
0 0 0 1px hsl(270, 80%, 60% / 0.2);
}
.generate-button:active {
transform: translateY(0) scale(0.98);
}
.number-input:focus-visible,
.select-input:focus-visible,
.generate-button:focus-visible {
outline: 2px solid hsl(270, 80%, 60%);
outline-offset: 2px;
}
@media (max-width: 767px) {
.generate-button {
padding: 1rem 2rem;
font-size: 1rem;
width: 100%;
max-width: 12rem;
}
.number-input,
.select-input {
padding: 0.875rem 1rem;
min-height: 3rem;
}
.select-input {
width: 9rem;
}
}
.gradient-grid {
display: grid;
grid-template-columns: 1fr;
gap: 1.5rem;
margin-bottom: 3rem;
}
@media (min-width: 640px) {
.gradient-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.gradient-grid {
grid-template-columns: repeat(4, 1fr);
}
}
.gradient-card {
position: relative;
height: 12rem;
border-radius: 1rem;
overflow: hidden;
box-shadow: 0 20px 40px -12px hsl(240, 10%, 0% / 0.5);
transition: all 0.5s ease;
animation: scale-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
animation-fill-mode: both;
backdrop-filter: blur(8px);
border: 1px solid hsl(240, 10%, 15% / 0.2);
cursor: pointer;
}
.gradient-card:hover {
transform: translateY(-0.5rem) scale(1.05);
box-shadow: 0 0 40px hsl(270, 80%, 60% / 0.3);
}
.card-overlay {
position: absolute;
inset: 0;
background: hsl(0, 0%, 0% / 0);
transition: all 0.3s ease;
}
.gradient-card:hover .card-overlay {
background: hsl(0, 0%, 0% / 0.2);
}
.copy-button {
position: absolute;
bottom: 1rem;
right: 1rem;
padding: 0.5rem 0.75rem;
border-radius: 0.5rem;
border: 1px solid hsl(0, 0%, 100% / 0.2);
background: hsl(0, 0%, 100% / 0.1);
backdrop-filter: blur(12px);
color: rgb(15, 3, 95);
font-size: 0.875rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
opacity: 0;
transform: translateY(0.5rem);
}
.gradient-card:hover .copy-button {
opacity: 1;
transform: translateY(0);
}
.copy-button:hover {
background: hsl(0, 0%, 100% / 0.2);
}
.gradient-info {
position: absolute;
bottom: 1rem;
left: 1rem;
opacity: 0;
transition: all 0.3s ease;
transform: translateY(0.5rem);
}
.gradient-card:hover .gradient-info {
opacity: 1;
transform: translateY(0);
}
.gradient-type {
background: hsl(0, 0%, 100% / 0.1);
backdrop-filter: blur(12px);
border-radius: 0.5rem;
padding: 0.25rem 0.75rem;
border: 1px solid hsl(0, 0%, 100% / 0.2);
color: white;
font-size: 0.875rem;
font-weight: 500;
text-transform: capitalize;
}
/* Load more section */
.load-more-container {
text-align: center;
margin-bottom: 2rem;
animation: fade-in 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
.load-more-button {
padding: 0.75rem 2rem;
border-radius: 0.75rem;
border: 1px solid hsl(240, 10%, 15% / 0.5);
background: hsl(240, 10%, 6% / 0.5);
backdrop-filter: blur(8px);
color: hsl(260, 94%, 6%);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.load-more-button:hover {
background: hsl(240, 10%, 6% / 0.8);
border-color: hsl(270, 80%, 60% / 0.5);
transform: scale(1.05);
}
/* Stats section */
.stats-container {
text-align: center;
animation: fade-in 0.6s cubic-bezier(0.23, 1, 0.32, 1);
}
.stats-badge {
display: inline-flex;
align-items: center;
gap: 0.5rem;
background: hsl(240, 10%, 6% / 0.3);
backdrop-filter: blur(8px);
border-radius: 9999px;
padding: 0.75rem 1.5rem;
border: 1px solid hsl(240, 10%, 15% / 0.2);
}
.stats-text {
font-size: 0.875rem;
color: hsl(240, 5%, 65%);
}
.stats-number {
font-weight: 600;
color: hsl(0, 0%, 98%);
}
/* Toast notification */
.toast-notification {
position: fixed;
top: 2rem;
right: 2rem;
background: hsl(240, 10%, 6%);
color: hsl(0, 0%, 98%);
padding: 1rem 1.5rem;
border-radius: 0.75rem;
border: 1px solid hsl(240, 10%, 15%);
box-shadow: 0 20px 40px -12px hsl(240, 10%, 0% / 0.5);
z-index: 1000;
animation: toast-slide-in 0.3s ease-out;
}
/* Animations */
@keyframes slide-up {
0% {
opacity: 0;
transform: translateY(2.5rem);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fade-in {
0% {
opacity: 0;
transform: translateY(1.25rem);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scale-in {
0% {
opacity: 0;
transform: scale(0.8);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes glow {
0% {
filter: brightness(1) drop-shadow(0 0 20px hsl(270, 80%, 60% / 0.3));
}
100% {
filter: brightness(1.1) drop-shadow(0 0 30px hsl(270, 80%, 60% / 0.5));
}
}
@keyframes gradient-shift {
0%, 100% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
}
@keyframes toast-slide-in {
0% {
opacity: 0;
transform: translateX(100%);
}
100% {
opacity: 1;
transform: translateX(0);
}
}
/* Responsive adjustments */
@media (max-width: 767px) {
.gradient-card {
height: 11.25rem;
}
.main-title {
font-size: 2rem;
}
.floating-element {
width: 12rem;
height: 12rem;
}
}
👉 Now you can import GradientGenerator.jsx
in your App.jsx
file and use it like any other React component.
💡 Key Learnings
We’ve built a Random Gradient Generator in React from scratch. This project helps you practice:
- React state management
(useState)
- Lifecycle hooks
(useEffect)
- Dynamic rendering with
.map()
- Copying text to clipboard
- Styling with CSS animations
Now you can:
- Deploy it on Netlify or Vercel and share with friends 🎉
🚀 Wrapping Up
In this guide, we built a Random Gradient Generator in React that creates stunning gradients with just one click. From generating random HEX colors to copying CSS with a custom toast notification, we kept things simple and focused on the fundamentals. 🎨✨
The cool part? 💡 We did it all with the basics: useState
, useEffect
, some simple logic, and a bit of CSS
magic. Nothing too fancy, but enough to make something that actually feels useful (and pretty cool to play with).
If you made it this far, I’d love to hear your thoughts, feedback, or suggestions for improvement.
Let’s keep learning, experimenting, and building awesome stuff together. 🚀
Happy Coding! 💻💖
Top comments (0)