Features
Feature grid showcasing product capabilities with icons.
Default Usage
import { Features } from '@/components/landing';
<Features />
Location: src/components/landing/Features.tsx
Features
- 6 feature cards in responsive grid (1-2-3 columns)
- Icon + title + description per card
- Color-coded icon backgrounds
- Scroll animation on reveal
Customization
The features array is defined in Features.tsx. To customize, edit the features array:
const features: Feature[] = [
{
icon: ShieldCheckIcon,
titleKey: 'authentication.title',
descriptionKey: 'authentication.description',
color: '#3b82f6',
},
// Add more features...
];
Variants
Variant 1: Grid with Icons (Default)
3-column grid with colored icon backgrounds.
// src/components/landing/Features.tsx - Default implementation
Best for: Standard feature showcases, 4-8 features
Variant 2: Bento Grid
Modern bento box layout with varied card sizes.
// src/components/landing/FeaturesBento.tsx
'use client';
import { useTranslations } from 'next-intl';
import { useScrollAnimation } from '@/hooks';
import {
ShieldCheckIcon,
BillingIcon,
CodeIcon,
UsersIcon,
ZapIcon,
GlobeIcon,
} from '@/components/icons';
export default function FeaturesBento() {
const t = useTranslations('landing.features');
const { elementRef, isVisible } = useScrollAnimation();
return (
<section id="features" className="py-20 sm:py-32 bg-zinc-50 dark:bg-zinc-900/50">
<div className="container mx-auto px-4">
<div
ref={elementRef as React.RefObject<HTMLDivElement>}
className={`scroll-fade-in ${isVisible ? 'visible' : ''}`}
>
<div className="text-center mb-16">
<h2 className="text-3xl sm:text-4xl font-bold mb-4">
{t('sectionTitle')}
</h2>
<p className="text-zinc-600 dark:text-zinc-400 text-lg max-w-2xl mx-auto">
{t('sectionSubtitle')}
</p>
</div>
{/* Bento Grid */}
<div className="grid grid-cols-1 md:grid-cols-6 gap-6 max-w-7xl mx-auto">
{/* Large card - spans 3 columns and 2 rows */}
<div className="md:col-span-3 md:row-span-2 bg-gradient-to-br from-blue-500 to-purple-600 rounded-2xl p-8 text-white">
<ShieldCheckIcon className="w-12 h-12 mb-4" />
<h3 className="text-2xl font-bold mb-3">
{t('authentication.title')}
</h3>
<p className="text-blue-100 text-lg">
{t('authentication.description')}
</p>
<div className="mt-8 space-y-2">
<div className="flex items-center gap-2">
JWT-based authentication
</div>
<div className="flex items-center gap-2">
Password reset & 2FA
</div>
<div className="flex items-center gap-2">
Session management
</div>
</div>
</div>
{/* Medium card */}
<div className="md:col-span-3 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-8">
<BillingIcon className="w-12 h-12 mb-4 text-green-600" />
<h3 className="text-xl font-bold mb-3">
{t('billing.title')}
</h3>
<p className="text-zinc-600 dark:text-zinc-400">
{t('billing.description')}
</p>
</div>
{/* Small cards */}
<div className="md:col-span-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-6">
<ZapIcon className="w-10 h-10 mb-3 text-yellow-600" />
<h3 className="font-bold mb-2">Fast Setup</h3>
<p className="text-sm text-zinc-600 dark:text-zinc-400">
Ship in minutes
</p>
</div>
<div className="md:col-span-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-6">
<CodeIcon className="w-10 h-10 mb-3 text-blue-600" />
<h3 className="font-bold mb-2">Modern Stack</h3>
<p className="text-sm text-zinc-600 dark:text-zinc-400">
React 19 + FastAPI
</p>
</div>
<div className="md:col-span-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-2xl p-6">
<GlobeIcon className="w-10 h-10 mb-3 text-purple-600" />
<h3 className="font-bold mb-2">i18n Ready</h3>
<p className="text-sm text-zinc-600 dark:text-zinc-400">
Multi-language support
</p>
</div>
{/* Horizontal card */}
<div className="md:col-span-6 bg-gradient-to-r from-zinc-900 to-zinc-800 dark:from-zinc-800 dark:to-zinc-900 text-white rounded-2xl p-8 flex items-center justify-between">
<div>
<h3 className="text-xl font-bold mb-2">
Complete Admin Panel
</h3>
<p className="text-zinc-400">
Manage users, roles, subscriptions, and more
</p>
</div>
<UsersIcon className="w-16 h-16 opacity-50" />
</div>
</div>
</div>
</div>
</section>
);
}
Best for: Modern apps, highlighting one main feature, varied content lengths
Variant 3: Feature List with Images
Alternating image + text sections for detailed features.
// src/components/landing/FeaturesDetailed.tsx
'use client';
import { useTranslations } from 'next-intl';
import Image from 'next/image';
import { useScrollAnimation } from '@/hooks';
import { CheckIcon } from '@/components/icons';
interface DetailedFeature {
titleKey: string;
descriptionKey: string;
benefits: string[];
image: string;
}
const features: DetailedFeature[] = [
{
titleKey: 'authentication.title',
descriptionKey: 'authentication.description',
benefits: [
'JWT-based auth with refresh tokens',
'Password reset via email',
'2FA support',
'Session management',
],
image: '/images/features/auth.png',
},
{
titleKey: 'billing.title',
descriptionKey: 'billing.description',
benefits: [
'Stripe integration',
'Subscription management',
'Customer portal',
'Webhook handling',
],
image: '/images/features/billing.png',
},
];
export default function FeaturesDetailed() {
const t = useTranslations('landing.features');
return (
<section id="features" className="py-20 sm:py-32">
<div className="container mx-auto px-4">
<div className="text-center mb-16">
<h2 className="text-3xl sm:text-4xl font-bold mb-4">
{t('sectionTitle')}
</h2>
<p className="text-zinc-600 dark:text-zinc-400 text-lg max-w-2xl mx-auto">
{t('sectionSubtitle')}
</p>
</div>
<div className="space-y-32">
{features.map((feature, index) => (
<FeatureRow
key={index}
feature={feature}
reverse={index % 2 === 1}
/>
))}
</div>
</div>
</section>
);
}
function FeatureRow({
feature,
reverse,
}: {
feature: DetailedFeature;
reverse?: boolean;
}) {
const t = useTranslations('landing.features');
const { elementRef, isVisible } = useScrollAnimation();
return (
<div
ref={elementRef as React.RefObject<HTMLDivElement>}
className={`scroll-fade-in ${isVisible ? 'visible' : ''}`}
>
<div
className={`grid gap-12 lg:gap-16 items-center ${
reverse ? 'lg:grid-cols-[1fr_1.2fr]' : 'lg:grid-cols-[1.2fr_1fr]'
}`}
>
{/* Content */}
<div className={reverse ? 'lg:order-2' : ''}>
<h3 className="text-2xl sm:text-3xl font-bold mb-4">
{t(feature.titleKey)}
</h3>
<p className="text-lg text-zinc-600 dark:text-zinc-400 mb-6">
{t(feature.descriptionKey)}
</p>
<ul className="space-y-3">
{feature.benefits.map((benefit, i) => (
<li key={i} className="flex items-start gap-3">
<div className="flex-shrink-0 w-6 h-6 rounded-full bg-green-100 dark:bg-green-900/30 flex items-center justify-center mt-0.5">
<CheckIcon className="w-4 h-4 text-green-600 dark:text-green-400" />
</div>
<span className="text-zinc-700 dark:text-zinc-300">
{benefit}
</span>
</li>
))}
</ul>
</div>
{/* Image */}
<div className={reverse ? 'lg:order-1' : ''}>
<div className="relative aspect-[4/3] rounded-2xl overflow-hidden shadow-2xl border border-zinc-200 dark:border-zinc-800">
<Image
src={feature.image}
alt={t(feature.titleKey)}
fill
className="object-cover"
/>
</div>
</div>
</div>
</div>
);
}
Best for: Feature-rich products, detailed explanations, enterprise applications
Variant 4: FeaturesAlt (Alternate Layout)
Alternative layout with icon on left, text on right.
import { FeaturesAlt } from '@/components/landing';
<FeaturesAlt />
Location: src/components/landing/FeaturesAlt.tsx
Choosing a Variant
| Variant | Use When |
|---|---|
| Grid with Icons | Standard feature list, 4-8 features |
| Bento Grid | Modern design, want to highlight one main feature |
| Feature List with Images | Need detailed explanations with screenshots |
| FeaturesAlt | Prefer horizontal layout with icon + text |