Retour au catalogue

Blog Magazine

Layout magazine avec un article featured en grand et une grille d'articles en dessous.

blogmedium Both Responsive a11y
editorialelegantuniversalagencygrid
Theme

How to build a magazine-style blog section in React

A magazine blog section in React pairs one wide featured article (two-column image + text) with a responsive auto-fill card grid below it. Framer Motion whileInView triggers staggered fade-up reveals on scroll without a single keyframe written by hand.

  • Stack: React + Framer Motion 11 + Lucide React, ~150 lines, zero extra dependencies.
  • Layout: CSS Grid, 1fr/1fr for the featured block, repeat(auto-fill, minmax(280px, 1fr)) for the card grid.
  • Theming: 100% CSS custom properties (--color-background, --color-accent, --radius-lg), zero hardcoded colors.
  • Accessible: semantic h2/h3/h4 hierarchy, lucide Calendar icon decorative only, links are native <a> elements.
  • Mobile caveat: the featured 2-column grid collapses to 1 column only if a breakpoint or media query is added, not included in the base 150 lines.

Blog Magazine is a React section that gives your content a newspaper-style hierarchy: one hero article spans the full width on the left, its editorial copy on the right, and three to six secondary cards fill a fluid grid below. It reads cleanly at a glance, scales from three to a dozen articles without touching layout code, and animates in on scroll through a handful of Framer Motion whileInView calls.

Anatomy

The section is split into three stacked regions. At the top, a section header with an h2 title and subtitle fades up on first scroll. Below it, a two-column CSS Grid card acts as the featured article: the left cell is a colored aspect-ratio placeholder (16/10), the right cell is a flex column with category badge, h3 headline, excerpt, and a date row using the Calendar icon from Lucide. The bottom region is a CSS Grid with repeat(auto-fill, minmax(280px, 1fr)) that maps the articles array into smaller cards, each with a 16/9 thumbnail, category, h4 title, and date. A centered "view all" link with an ArrowRight icon closes the section.

How it works

Every visual block is a motion.a or motion.div with initial={{ opacity: 0, y: 16 }} and whileInView={{ opacity: 1, y: 0 }}. Viewport is configured with once:true so the animation fires exactly once. The card grid staggers each item by multiplying its index by 0.06 seconds in the transition delay. A shared EASE constant ([0.16, 1, 0.3, 1], a fast-out/slow-in cubic) gives the reveals a snappy editorial feel rather than a generic linear fade. The component is presentational: it accepts a featured article object and an articles array as props, falling back to inline defaults when neither is provided.

How to build it in React

  1. Define your data interfaces and props

    Create an Article interface (title, excerpt, date, category, imageColor) and a BlogMagazineProps interface that wraps sectionTitle, sectionSubtitle, featured, and articles. Keep imageColor as a CSS string so you can pass any custom property or hex value from the parent.

    interface Article {
      title: string;
      excerpt: string;
      date: string;
      category: string;
      imageColor: string;
    }
    
    interface BlogMagazineProps {
      sectionTitle?: string;
      featured?: Article;
      articles?: Article[];
    }
  2. Build the featured article with a 2-column CSS Grid

    Use a motion.a wrapping a div with gridTemplateColumns:'1fr 1fr'. The left cell is a div with aspectRatio:'16/10' and a semi-transparent background color. The right cell is a flex column with the category badge, h3 headline, excerpt paragraph, and a Calendar + date row.

    <motion.a
      initial={{ opacity: 0, y: 20 }}
      whileInView={{ opacity: 1, y: 0 }}
      viewport={{ once: true }}
      transition={{ duration: 0.6, delay: 0.08, ease: EASE }}
      style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '2rem' }}
    >
  3. Render the card grid with staggered delays

    Map the articles array and wrap each card in a motion.a. Use i * 0.06 as the transition delay so cards reveal sequentially from left to right. The grid uses repeat(auto-fill, minmax(280px, 1fr)) so it reflows automatically from one to four columns.

    {articles.map((article, i) => (
      <motion.a
        key={i}
        initial={{ opacity: 0, y: 16 }}
        whileInView={{ opacity: 1, y: 0 }}
        viewport={{ once: true }}
        transition={{ duration: 0.45, delay: i * 0.06, ease: EASE }}
      >
        ...
      </motion.a>
    ))}
  4. Use CSS custom properties for all colors and spacing

    Never hardcode a hex value. Use var(--color-background), var(--color-foreground), var(--color-accent), var(--color-border), var(--radius-lg), and var(--section-padding-y) throughout. This lets any parent theme preset switch the look without touching the component source.

When to use it

Use this layout when your site has a genuine content program: a blog, a newsroom, a changelog, or a resource hub with at least four to six articles. It works best in the middle of a landing page as a trust signal, or as the main template for a blog index. Skip it on pages where the blog is an afterthought with one or two posts, or where you need a masonry or infinite-scroll feed, this grid expects a predictable count. On mobile, add a media query to switch the featured grid from two columns to one; the card grid already reflows on its own.

Used by

  • Stripe, Features a large hero article above a grid of recent posts, a direct application of this magazine hierarchy.
  • Vercel, Blog index with a pinned featured post at the top and a masonry-style grid of secondary articles below.
  • Linear, Magazine-style blog index: one wide feature card, then a two or three-column grid of recent entries.
  • Liveblocks, Featured post spans full width at the top, smaller article cards fill a responsive grid below it.

FAQ

How do I make the featured block stack vertically on mobile?

The featured block uses CSS Grid with gridTemplateColumns:'1fr 1fr' set inline. Add a media query (or a Tailwind responsive class if you switch to className) that overwrites this to gridTemplateColumns:'1fr' below your mobile breakpoint. The card grid below already reflows via auto-fill.

Can I replace the imageColor placeholders with real images?

Replace the colored div with an img or next/image element set to objectFit:'cover' and the same aspect-ratio style. Swap imageColor in the Article interface for an imageUrl string, and you are done.

Why does each card animate separately instead of all at once?

The delay: i * 0.06 expression staggers the cards by 60ms per index. This gives the grid a cascade feel that reads as deliberate rather than a bulk pop-in, making the layout feel lighter and more editorial.

Does the component work without any articles passed in?

The featured article falls back to an inline default object so the section always renders something. The articles array defaults to an empty array, so the card grid simply renders nothing without throwing. Pass at least three articles to make the layout look intentional.

"use client";

import { motion } from "framer-motion";
import { Calendar, ArrowRight } from "lucide-react";

interface Article {
  title: string;
  excerpt: string;
  date: string;
  category: string;
  imageColor: string;
}

interface BlogMagazineProps {
  sectionTitle?: string;
  sectionSubtitle?: string;
  featured?: Article;
  articles?: Article[];
}

const EASE = [0.16, 1, 0.3, 1] as const;

Code complet réservé à Pro

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

Passer en Pro, 9,99€/mois

Reviews

React Magazine Blog Layout, Featured + Grid, Tutorial