Retour au catalogue

About Team Intro

Section about avec focus sur l'equipe : photo de groupe et chiffres cles.

aboutsimple Both Responsive a11y
corporateorganicuniversalagencysplit
Theme

How to build an about section with team intro in React

An about-team-intro section in React uses a two-column grid: the left column holds a badge, heading, description and animated highlight rows that slide in from the left on scroll; the right column shows a square team image that fades and scales into view. Both columns use Framer Motion's whileInView with once:true so they animate exactly once as the user scrolls past.

  • Stack: React 18, Framer Motion 11, Lucide React, Tailwind v4. ~145 lines, zero extra runtime dependencies.
  • Icons are resolved dynamically from the lucide-react namespace, any Lucide icon name can be passed as a string prop.
  • Fully themeable via CSS custom properties (--color-accent, --color-background-alt, --radius-md, etc.), no hardcoded colours.
  • Accessible: semantic section + h2, icons are decorative (aria-hidden by default via Lucide), responsive down to 375px.
  • All props are optional, badge, image and highlights can be omitted without breaking the layout.

About Team Intro is a two-column React section that pairs a text block with a team photo to give visitors a face behind the product. The left side carries badge, heading, paragraph and a vertical list of icon-backed highlights; the right side frames a square image that scales subtly into view as the user scrolls. Together they communicate trust and culture without adding cognitive overhead.

Anatomy

A full-width section with a max-w-6xl centered container holds a responsive CSS grid (1 column on mobile, 2 on md+). The left column is a motion.div that wraps an optional badge pill, h2 heading, description paragraph and a flex-col list of highlight rows. Each highlight row is its own motion.div containing an optional icon container (10×10 rounded square with a tinted accent background) and a text pair (semibold label + muted description). The right column is a square motion.div with overflow:hidden that renders the team image as a CSS background-image.

How it works

Two distinct Framer Motion entrance patterns run in parallel. The left column uses initial:{opacity:0, y:20} with whileInView:{opacity:1, y:0}, a 500ms duration and a viewport margin of -80px so the animation fires slightly before the element enters the viewport. Each highlight row adds a staggered delay of 100ms + (index × 80ms) and slides in from x:-12 instead of y, giving the list a cascading feel. The right column uses initial:{opacity:0, scale:0.96} with a custom cubic-bezier ease [0.16, 1, 0.3, 1] over 600ms for a subtle spring-like pop. All animations fire once (once:true) regardless of scroll-back.

How to build it in React

  1. Set up the two-column grid

    Create a section with a centered max-w-6xl container. Inside, use a CSS grid that collapses to one column on mobile and expands to two on md breakpoint. Pass items-center so the columns align vertically when they have unequal heights.

    <div className="grid grid-cols-1 gap-12 items-center md:grid-cols-2">
      {/* text column */}
      {/* image column */}
    </div>
  2. Animate the text column on scroll

    Wrap the text column in a motion.div with initial opacity 0 and y offset of 20px. Use whileInView to restore them and set viewport once:true with a -80px margin so the fade starts before the element fully enters the screen.

    <motion.div
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true, margin: "-80px" }}
      transition={{ duration: 0.5 }}
    >
  3. Stagger the highlight rows

    Each highlight row is a separate motion.div that slides in from x:-12. Add a delay computed from the item index, 0.1s base plus 0.08s per item. This creates a natural cascade without any extra orchestration API.

    <motion.div
      initial={{ opacity: 0, x: -12 }}
      whileInView={{ opacity: 1, x: 0 }}
      viewport={{ once: true }}
      transition={{ delay: 0.1 + i * 0.08, duration: 0.4 }}
    >
  4. Reveal the team image with a scale pop

    The image column is a square motion.div (aspect-square) with overflow:hidden and a border radius token. Set initial scale to 0.96 and animate to 1 using a custom ease [0.16, 1, 0.3, 1] over 600ms, this mimics a spring without importing the spring API.

    <motion.div
      initial={{ opacity: 0, scale: 0.96 }}
      whileInView={{ opacity: 1, scale: 1 }}
      viewport={{ once: true, margin: "-60px" }}
      transition={{ duration: 0.6, ease: [0.16, 1, 0.3, 1] }}
      className="aspect-square rounded-[var(--radius-xl)] overflow-hidden"
    >

When to use it

Use this section mid-page on landing pages for agencies, SaaS products and startups that need to establish trust before a pricing or testimonials block. It works well as the second or third section, after a hero. Skip it if your audience already knows the team well (internal tools, developer docs) or if the page is purely conversion-focused and a team photo would be a distraction. On mobile the grid collapses to single column, so verify the image aspect-ratio looks intentional at 375px rather than cropped awkwardly.

Used by

  • Stripe, Split layout pairing editorial text with a team/office photo on the about page to humanise a developer-facing brand.
  • Linear, Company narrative with icon-backed value bullets alongside imagery, the exact pattern this component implements.
  • Loom, Two-column about sections with staggered text reveals and team photography to communicate culture.

FAQ

How do I resolve a Lucide icon by string name at runtime?

Cast the lucide-react namespace to a Record<string, React.ElementType> and look up the icon name as a key. Return null if the key is missing so the layout degrades gracefully without an icon container.

Can I replace the image with a video or a grid of headshots?

The right column is a plain div with overflow:hidden, swap the background-image div for any element. A video (autoplay muted loop) or a CSS grid of headshots fits without touching the animation code.

Why use CSS background-image instead of an img tag for the photo?

background-image with background-size:cover handles arbitrary aspect ratios without layout shift. For production, swap it for a Next.js Image component with objectFit:cover to get automatic optimisation and lazy loading.

The stagger animation feels too slow on long lists. How do I tune it?

Reduce the per-item delay increment from 0.08 to 0.05 or less, and cap the total delay with Math.min(0.1 + i * 0.05, 0.4) so items beyond the fifth don't wait noticeably longer.

"use client";

import React from "react";
import { motion } from "framer-motion";
import * as LucideIcons from "lucide-react";

interface HighlightItem {
  icon?: string;
  label: string;
  description: string;
}

interface AboutTeamIntroProps {
  badge?: string;
  title?: string;
  description?: string;
  image?: string;
  highlights?: HighlightItem[];
}

function getIcon(name?: string) {
  if (!name) return null;

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reviews

React About Section with Team Intro, Split Layout