Features

Feature grid showcasing product capabilities with icons.

Default Usage

import { Features } from '@/components/landing';

<Features />

Location: src/components/landing/Features.tsx

Features

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