Contest Entry Form
A hero with a contest entry form for name, email, and answer fields. Drives signups and excitement around a giveaway or promotional campaign.
A hero with a contest entry form for name, email, and answer fields. Drives signups and excitement around a giveaway or promotional campaign.
Search blocks by description or jump to a page.
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { Separator } from '@/components/ui/separator';
import { Badge } from '@/components/ui/badge';
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card';
import {
GiftIcon,
TrophyIcon,
ClockIcon,
ShareIcon,
CheckIcon,
UsersIcon,
} from 'lucide-react';
export default function HeroFormContestEntry() {
const [formData, setFormData] = useState({
firstName: '',
lastName: '',
email: '',
agreeToTerms: false,
agreeToMarketing: false,
});
const [submitted, setSubmitted] = useState(false);
const [loading, setLoading] = useState(false);
const [shareMethod, setShareMethod] = useState<string | null>(null);
// Contest details
const contestEndDate = 'June 30, 2024';
const prizeItems = [
{
id: 'grand',
name: 'Grand Prize',
description: 'Latest Pro Tablet',
image: '/images/contest/tablet.jpg',
value: '$999',
},
{
id: 'second',
name: 'Second Prize',
description: 'Wireless Earbuds',
image: '/images/contest/earbuds.jpg',
value: '$249',
},
{
id: 'third',
name: 'Third Prize (5 winners)',
description: 'Smart Fitness Band',
image: '/images/contest/fitness.jpg',
value: '$99',
},
];
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({
...prev,
[name]: value,
}));
};
const handleCheckboxChange = (name: string, checked: boolean) => {
setFormData((prev) => ({
...prev,
[name]: checked,
}));
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
setLoading(true);
// Simulate API call
setTimeout(() => {
setLoading(false);
setSubmitted(true);
}, 1000);
};
const handleShare = (method: string) => {
setShareMethod(method);
// In a real implementation, this would open the respective share dialog
console.log(`Sharing via ${method}`);
// Reset after a brief delay
setTimeout(() => {
setShareMethod(null);
}, 2000);
};
return (
<div className="relative overflow-hidden bg-gradient-to-b from-yellow-50 to-white dark:from-yellow-950 dark:to-slate-900">
{/* Background elements */}
<div className="bg-grid-slate-200 dark:bg-grid-slate-800/20 absolute inset-0 bg-[size:24px_24px] opacity-10"></div>
<div className="absolute top-0 right-0 left-0 h-1 bg-gradient-to-r from-yellow-400 via-orange-500 to-red-500"></div>
<div className="absolute -top-40 -right-40 h-80 w-80 rounded-full bg-yellow-200 opacity-20 blur-3xl dark:bg-yellow-900"></div>
<div className="absolute -bottom-40 -left-40 h-80 w-80 rounded-full bg-orange-200 opacity-20 blur-3xl dark:bg-orange-900"></div>
<div className="relative z-10 container mx-auto px-4 py-16 md:px-6 lg:py-24 2xl:max-w-[1400px]">
<div className="mx-auto grid max-w-6xl items-center gap-12 lg:grid-cols-2">
{/* Left column - Content */}
<div>
<Badge
variant="outline"
className="mb-6 border-yellow-200 bg-yellow-50 px-3 py-1 text-sm font-medium text-yellow-700 dark:border-yellow-800 dark:bg-yellow-900/20 dark:text-yellow-300"
>
<TrophyIcon className="mr-1 h-3.5 w-3.5" />
Enter Our Giveaway
</Badge>
<h1 className="mb-6 text-4xl font-bold tracking-tight lg:text-5xl">
Win Amazing Prizes in our{' '}
<span className="text-yellow-600 dark:text-yellow-400">
Summer Tech
</span>{' '}
Giveaway!
</h1>
<div className="mb-8 flex w-fit items-center gap-2 rounded-full border bg-white px-3 py-1.5 text-sm dark:bg-slate-800">
<ClockIcon className="h-4 w-4 text-yellow-600" />
<span>Contest ends on {contestEndDate}</span>
</div>
<p className="text-muted-foreground mb-8 text-xl">
Enter for your chance to win one of our amazing tech prizes.
It's quick, easy, and completely free to enter!
</p>
<div className="mb-10 space-y-6">
<div className="flex items-start">
<div className="mr-3 flex-shrink-0 rounded-full bg-yellow-100 p-1 dark:bg-yellow-900/30">
<CheckIcon className="h-4 w-4 text-yellow-600 dark:text-yellow-400" />
</div>
<div>
<p className="text-sm">
<span className="font-medium">Free entry</span> - no
purchase necessary
</p>
</div>
</div>
<div className="flex items-start">
<div className="mr-3 flex-shrink-0 rounded-full bg-yellow-100 p-1 dark:bg-yellow-900/30">
<CheckIcon className="h-4 w-4 text-yellow-600 dark:text-yellow-400" />
</div>
<div>
<p className="text-sm">
<span className="font-medium">Multiple winners</span> -
increased chances to win
</p>
</div>
</div>
<div className="flex items-start">
<div className="mr-3 flex-shrink-0 rounded-full bg-yellow-100 p-1 dark:bg-yellow-900/30">
<CheckIcon className="h-4 w-4 text-yellow-600 dark:text-yellow-400" />
</div>
<div>
<p className="text-sm">
<span className="font-medium">Worldwide shipping</span> -
open to international entries
</p>
</div>
</div>
<div className="flex items-start">
<div className="mr-3 flex-shrink-0 rounded-full bg-yellow-100 p-1 dark:bg-yellow-900/30">
<CheckIcon className="h-4 w-4 text-yellow-600 dark:text-yellow-400" />
</div>
<div>
<p className="text-sm">
<span className="font-medium">Bonus entries</span> - share
with friends for extra chances
</p>
</div>
</div>
</div>
{/* Prizes showcase */}
<div>
<h3 className="mb-4 text-lg font-semibold">
Prizes Up For Grabs
</h3>
<div className="grid gap-4 md:grid-cols-3">
{prizeItems.map((prize) => (
<Card
key={prize.id}
className="overflow-hidden border pt-0 shadow-sm"
>
<div className="relative aspect-[4/3] bg-slate-100 dark:bg-slate-800">
{/* Placeholder for prize image */}
<div className="absolute inset-0 flex items-center justify-center bg-slate-100 dark:bg-slate-800">
<GiftIcon className="h-12 w-12 text-yellow-500" />
</div>
</div>
<CardContent className="">
<h4 className="text-sm font-medium">{prize.name}</h4>
<p className="text-muted-foreground text-xs">
{prize.description}
</p>
<p className="mt-1 text-xs font-medium text-yellow-600 dark:text-yellow-400">
Value: {prize.value}
</p>
</CardContent>
</Card>
))}
</div>
</div>
<div className="mt-8 lg:hidden">
<Button
onClick={() =>
document
.getElementById('entry-form')
?.scrollIntoView({ behavior: 'smooth' })
}
className="w-full bg-yellow-600 text-white hover:bg-yellow-700"
>
Enter Now - It's Free
</Button>
</div>
</div>
{/* Right column - Form */}
<div id="entry-form">
{!submitted ? (
<Card className="overflow-hidden border p-0 shadow-md">
<CardHeader className="bg-gradient-to-r from-yellow-500 to-orange-500 p-6 text-white">
<CardTitle className="text-2xl">Enter to Win</CardTitle>
<CardDescription className="text-white/80">
{' '}
out the form below to participate
</CardDescription>
</CardHeader>
<CardContent className="p-6">
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid gap-4 md:grid-cols-2">
<div>
<Label className="mb-1.5" htmlFor="firstName">
First Name*
</Label>
<Input
id="firstName"
name="firstName"
value={formData.firstName}
onChange={handleChange}
placeholder="John"
required
/>
</div>
<div>
<Label className="mb-1.5" htmlFor="lastName">
Last Name*
</Label>
<Input
id="lastName"
name="lastName"
value={formData.lastName}
onChange={handleChange}
placeholder="Doe"
required
/>
</div>
</div>
<div>
<Label className="mb-1.5" htmlFor="email">
Email Address*
</Label>
<Input
id="email"
name="email"
type="email"
value={formData.email}
onChange={handleChange}
placeholder="john@example.com"
required
/>
</div>
<div className="flex items-start space-x-2 pt-2">
<Checkbox
id="terms"
checked={formData.agreeToTerms}
onCheckedChange={(checked) =>
handleCheckboxChange(
'agreeToTerms',
checked as boolean
)
}
className="mt-1"
/>
<Label
htmlFor="terms"
className="text-sm leading-tight font-normal"
>
<div>
I agree to the contest{' '}
<a
href="#"
className="text-yellow-600 hover:underline dark:text-yellow-400"
>
terms and conditions
</a>{' '}
and have read the{' '}
<a
href="#"
className="text-yellow-600 hover:underline dark:text-yellow-400"
>
privacy policy*
</a>
</div>
</Label>
</div>
<div className="flex items-start space-x-2">
<Checkbox
id="marketing"
checked={formData.agreeToMarketing}
onCheckedChange={(checked) =>
handleCheckboxChange(
'agreeToMarketing',
checked as boolean
)
}
className="mt-1"
/>
<Label
htmlFor="marketing"
className="text-sm leading-tight font-normal"
>
I would like to receive marketing emails about products,
services, and future contests. (Optional)
</Label>
</div>
<Button
type="submit"
className="w-full bg-yellow-600 text-white hover:bg-yellow-700"
disabled={
!formData.email || !formData.agreeToTerms || loading
}
>
{loading ? (
<span className="flex items-center gap-2">
<svg
className="h-5 w-5 animate-spin text-white"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
>
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
></circle>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
></path>
</svg>
Submitting...
</span>
) : (
<span>Enter the Contest</span>
)}
</Button>
</form>
</CardContent>
<div className="border-t bg-slate-50 px-6 py-4 dark:bg-slate-900/50">
<div className="flex items-center gap-4">
<div className="flex h-10 w-10 items-center justify-center rounded-full bg-yellow-100 dark:bg-yellow-900/30">
<UsersIcon className="h-5 w-5 text-yellow-600 dark:text-yellow-400" />
</div>
<div>
<p className="text-sm">
<span className="font-medium">1,847 people</span> have
already entered
</p>
</div>
</div>
</div>
</Card>
) : (
<Card className="overflow-hidden border p-0 shadow-md">
<div className="p-8 text-center">
<div className="mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full bg-yellow-100 dark:bg-yellow-900/30">
<TrophyIcon className="h-8 w-8 text-yellow-600 dark:text-yellow-400" />
</div>
<h3 className="mb-2 text-2xl font-bold">You're In!</h3>
<p className="text-muted-foreground mb-6">
Your entry has been received. Good luck!
</p>
<div className="mb-6 rounded-lg bg-yellow-50 p-6 dark:bg-yellow-900/20">
<h4 className="mb-4 font-medium">
Increase Your Chances of Winning
</h4>
<p className="mb-4 text-sm">
Share this contest with your friends and family to get
bonus entries!
</p>
<div className="flex justify-center space-x-3">
<Button
variant="outline"
size="sm"
className="flex items-center gap-2 border-yellow-200 text-yellow-700 hover:bg-yellow-50 dark:border-yellow-900 dark:text-yellow-400 dark:hover:bg-yellow-900/30"
onClick={() => handleShare('facebook')}
>
{shareMethod === 'facebook' ? (
<CheckIcon className="h-4 w-4" />
) : (
<ShareIcon className="h-4 w-4" />
)}
Facebook
</Button>
<Button
variant="outline"
size="sm"
className="flex items-center gap-2 border-yellow-200 text-yellow-700 hover:bg-yellow-50 dark:border-yellow-900 dark:text-yellow-400 dark:hover:bg-yellow-900/30"
onClick={() => handleShare('twitter')}
>
{shareMethod === 'twitter' ? (
<CheckIcon className="h-4 w-4" />
) : (
<ShareIcon className="h-4 w-4" />
)}
Twitter
</Button>
<Button
variant="outline"
size="sm"
className="flex items-center gap-2 border-yellow-200 text-yellow-700 hover:bg-yellow-50 dark:border-yellow-900 dark:text-yellow-400 dark:hover:bg-yellow-900/30"
onClick={() => handleShare('email')}
>
{shareMethod === 'email' ? (
<CheckIcon className="h-4 w-4" />
) : (
<ShareIcon className="h-4 w-4" />
)}
Email
</Button>
</div>
</div>
<Separator className="my-6" />
<div className="space-y-4 text-left">
<h4 className="font-medium">What happens next?</h4>
<div className="space-y-4">
<div className="flex items-start gap-2">
<div className="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-yellow-100 text-xs font-medium dark:bg-yellow-900/30">
1
</div>
<p className="text-sm">
We'll send you a confirmation email shortly to{' '}
{formData.email}
</p>
</div>
<div className="flex items-start gap-2">
<div className="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-yellow-100 text-xs font-medium dark:bg-yellow-900/30">
2
</div>
<p className="text-sm">
Winners will be randomly selected after the contest
ends on {contestEndDate}
</p>
</div>
<div className="flex items-start gap-2">
<div className="mt-0.5 flex h-6 w-6 items-center justify-center rounded-full bg-yellow-100 text-xs font-medium dark:bg-yellow-900/30">
3
</div>
<p className="text-sm">
Winners will be notified by email within 5 days after
the drawing
</p>
</div>
</div>
</div>
</div>
</Card>
)}
{/* Contest rules teaser */}
<div className="mt-6 text-center">
<p className="text-muted-foreground text-sm">
<a
href="#"
className="text-yellow-600 hover:underline dark:text-yellow-400"
>
View complete contest rules
</a>{' '}
| No purchase necessary. Void where prohibited.
</p>
</div>
</div>
</div>
</div>
</div>
);
}
Contest Entry Form is a two-column hero split between a pitch column on the left and an entry card on the right. The left column opens with a badge and headline, followed by a deadline pill showing the contest close date, a short description, a four-item checklist of entry benefits, and a three-card prize showcase displaying the grand prize, second prize, and five third-place prizes each with a value label. The right column holds a card with a colored header, first name and last name fields in a two-column row, an email field, a required terms checkbox, an optional marketing checkbox, a submit button, and a live entry count in the card footer. After submission the form swaps for a confirmation view with share buttons for Facebook, Twitter, and email, and a numbered three-step explanation of what happens next.
The headline copy, deadline date, prize list, entry-count number, and checkbox label text are all independent strings you can replace without touching the layout. You can remove any prize card, drop the marketing checkbox entirely, or swap the share buttons for different channels.
Reach for this block when a campaign or giveaway is the primary action on the page and you need entrants to commit their contact details up front. It suits a product analytics SaaS running a referral promotion, a seasonal giveaway landing page, or any campaign where the prize lineup needs to be visible alongside the form rather than buried below the fold. Compared to a plain email-capture hero, the prize cards and checklist do the persuasion work so visitors understand what they are entering before they fill anything in.
A natural flow around it on a Marketing (Legacy) page:
Before
After
All three prize tiers plus the form fields are independently editable, so the block adapts to contests of different sizes and shapes:
Tip: set the deadline date to a real date close in the future so the closing pill creates a genuine sense of urgency rather than looking like placeholder copy.