EmpireUI
Get Pro
← Blog7 min read#glassmorphism#social-feed#react

Glassmorphism Social Media Feed: Post Cards with Blur

Build a glassmorphism social media feed with frosted blur post cards using React and Tailwind. Real code, real values — no fluff, just a feed that looks good.

Frosted glass UI cards arranged in a social media feed layout against a colorful blurred background

Why Glassmorphism Works for Social Feeds

Honestly, glassmorphism is one of the few UI trends that actually earns its place in a social feed layout — not because it looks trendy, but because the depth it creates genuinely helps users distinguish content layers at a glance.

A social feed is dense. You've got avatars, post text, media thumbnails, reaction counts, timestamps, and share buttons all crammed into repeating card units. Without visual separation, the whole thing collapses into a wall of noise. Frosted glass cards with backdrop-filter: blur(12px) and a semi-transparent background create soft boundaries without the hard shadow boxes that feel dated.

If you're already curious about the origins of the style itself, the what is glassmorphism article covers the design theory. This article is purely about implementation — building a feed that renders correctly, stays accessible, and doesn't murder performance on mobile.

The trick is treating each post card as its own frosted pane sitting above a shared gradient or media background. That relationship between card and background is what makes the effect feel intentional rather than decorative.

Setting Up the Background Layer

Glassmorphism is entirely dependent on having something visually interesting behind the glass. A flat white background with a blur filter just gives you white. You need color, gradients, or imagery underneath.

For a social feed, a fixed gradient background works well because it doesn't compete with the content the way a photo does. Here's a starting setup in Tailwind v4.0.2 with a custom gradient and the feed container:

// FeedLayout.tsx
export function FeedLayout({ children }: { children: React.ReactNode }) {
  return (
    <div className="min-h-screen bg-gradient-to-br from-violet-900 via-purple-800 to-indigo-900 relative overflow-hidden">
      {/* Decorative blobs behind everything */}
      <div className="absolute top-20 left-10 w-72 h-72 bg-pink-500/30 rounded-full blur-3xl pointer-events-none" />
      <div className="absolute bottom-10 right-16 w-96 h-96 bg-cyan-400/20 rounded-full blur-3xl pointer-events-none" />

      <div className="relative z-10 max-w-xl mx-auto px-4 py-10 flex flex-col gap-5">
        {children}
      </div>
    </div>
  );
}

The two decorative blobs use blur-3xl and low opacity to create soft ambient color. They're absolutely positioned with pointer-events-none so they never interfere with click targets. The feed column itself sits in z-10, above the blobs.

Building the Glassmorphism Post Card Component

Each post card needs three CSS properties to actually look like frosted glass: a semi-transparent background, backdrop-filter: blur(), and a thin light border. Skip any one of them and you've got something that looks broken, not designed.

Here's a full post card component. It handles the avatar, author name, timestamp, body text, and an action row for likes and comments. The glass effect is applied via a single Tailwind utility class pattern combined with an inline style for the exact rgba values:

// PostCard.tsx
import { Heart, MessageCircle, Share2 } from 'lucide-react';

interface PostCardProps {
  avatar: string;
  author: string;
  handle: string;
  timestamp: string;
  body: string;
  likes: number;
  comments: number;
}

export function PostCard({
  avatar, author, handle, timestamp, body, likes, comments
}: PostCardProps) {
  return (
    <article
      className="rounded-2xl p-5 border border-white/20 shadow-xl"
      style={{
        background: 'rgba(255, 255, 255, 0.10)',
        backdropFilter: 'blur(14px)',
        WebkitBackdropFilter: 'blur(14px)',
      }}
    >
      {/* Header */}
      <div className="flex items-center gap-3 mb-4">
        <img
          src={avatar}
          alt={`${author} avatar`}
          className="w-10 h-10 rounded-full ring-2 ring-white/30 object-cover"
        />
        <div>
          <p className="text-white font-semibold text-sm leading-tight">{author}</p>
          <p className="text-white/50 text-xs">{handle} · {timestamp}</p>
        </div>
      </div>

      {/* Body */}
      <p className="text-white/85 text-sm leading-relaxed mb-5">{body}</p>

      {/* Actions */}
      <div className="flex items-center gap-6 text-white/60 text-xs">
        <button className="flex items-center gap-1.5 hover:text-pink-400 transition-colors">
          <Heart size={15} />
          <span>{likes}</span>
        </button>
        <button className="flex items-center gap-1.5 hover:text-sky-400 transition-colors">
          <MessageCircle size={15} />
          <span>{comments}</span>
        </button>
        <button className="flex items-center gap-1.5 hover:text-white transition-colors ml-auto">
          <Share2 size={15} />
        </button>
      </div>
    </article>
  );
}

The rgba(255, 255, 255, 0.10) background is intentionally low opacity — 0.10, not 0.15 — because the gradient background is already light enough that 0.15 starts looking opaque on mobile screens where GPU-accelerated blur can behave differently.

Assembling the Full Feed

With the layout and card in place, assembling the feed is just mapping over data. No magic here. Keep the gap between cards at 20px (the gap-5 in Tailwind) — tighter than that and the blur edges visually bleed into each other.

// SocialFeed.tsx
import { FeedLayout } from './FeedLayout';
import { PostCard } from './PostCard';

const POSTS = [
  {
    id: '1',
    avatar: 'https://i.pravatar.cc/80?img=12',
    author: 'Maya Chen',
    handle: '@mayac',
    timestamp: '2h ago',
    body: 'Just shipped the dark mode toggle with zero JavaScript. Pure CSS custom properties. Feels illegal.',
    likes: 142,
    comments: 38,
  },
  {
    id: '2',
    avatar: 'https://i.pravatar.cc/80?img=27',
    author: 'Tariq Osei',
    handle: '@tariqo',
    timestamp: '4h ago',
    body: 'Unpopular opinion: blur effects are fine. It's the gradient backgrounds people can't stop ruining.',
    likes: 89,
    comments: 61,
  },
  {
    id: '3',
    avatar: 'https://i.pravatar.cc/80?img=44',
    author: 'Sonia V.',
    handle: '@soniav',
    timestamp: '6h ago',
    body: 'backdrop-filter support is finally solid across all major browsers. No more conditional CSS hacks. We're free.',
    likes: 204,
    comments: 19,
  },
];

export default function SocialFeed() {
  return (
    <FeedLayout>
      {POSTS.map((post) => (
        <PostCard key={post.id} {...post} />
      ))}
    </FeedLayout>
  );
}

That's the whole feed. Three posts, real data shapes, a layout that handles overflow without horizontal scroll, and glass cards that work in production.

Handling the Blur Performance Problem

Here's the thing: backdrop-filter: blur() is GPU-accelerated, but it's not free. On a feed with 20+ cards in the DOM, you'll hit paint performance issues on mid-range Android devices. The blur composite layer forces the browser to rasterize everything behind each card independently.

Two practical fixes. First, use content-visibility: auto on the feed container so off-screen cards don't participate in layout or paint. Second, reduce the blur radius on mobile. blur(14px) looks great on a 2560x1440 monitor. On a 360px viewport, blur(8px) is visually identical and measurably cheaper.

/* globals.css */
@media (max-width: 640px) {
  .glass-card {
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
  }
}

.feed-container {
  content-visibility: auto;
  contain-intrinsic-size: 0 600px;
}

Also worth noting: if you're virtualizing the feed (React Virtuoso, TanStack Virtual), you mostly sidestep the problem because only the visible cards are in the DOM at any time. That's the right long-term answer for feeds with real data volumes.

For a deeper comparison of how glassmorphism stacks up against other layered styles in terms of rendering cost, glassmorphism vs neumorphism has a practical breakdown.

Dark Mode and Theme Switching

Glassmorphism is one of the few styles that adapts to dark mode without a complete redesign. The frosted pane is defined by its relationship to what's behind it. Change the background gradient, the glass cards shift automatically.

The only thing that needs explicit dark mode handling is text opacity. text-white/85 works on a dark gradient. On a light background — say, a pastel gradient for a light mode variant — you'd flip to text-black/80 and adjust the border to border-black/10.

If you're wiring this into a theme toggle system, check out theme toggle in React for a cookie-based implementation that avoids the flash-of-wrong-theme issue. The glassmorphism values you want to vary are the background rgba alpha (0.10 for dark, 0.60 for light), the blur radius (14px stays constant), and the border color.

Don't try to do this with Tailwind's dark: prefix alone. The rgba background has to go through CSS custom properties or inline styles. You can't express rgba(255,255,255,0.10) vs rgba(0,0,0,0.08) as a Tailwind dark variant without a custom plugin.

Media Attachments Inside Glass Cards

Post cards with images need an extra consideration. If you put an <img> inside the frosted card without clipping it, the image will render over the border-radius corners and look broken — especially on Safari.

The fix is a nested container with overflow-hidden rounded-xl applied to the image wrapper. Don't apply overflow-hidden to the card itself because that clips the subtle shadow that reinforces the glass edge.

Also, images inside glass cards should have a slight opacity reduction — around opacity-90 — so they don't look pasted in. The frosted glass aesthetic suggests diffusion. A full-opacity image inside a semi-transparent container creates an uncanny visual conflict.

For feeds where posts might include a particles background or video previews, test on real devices. Video behind a backdrop-filter blur can cause frame drops that no amount of CSS optimization fixes. In that case, fall back to a static poster image for the background layer.

Getting Glassmorphism Components from Empire UI

Building all of this from scratch is fine for learning the mechanics. For production projects, Empire UI ships pre-built glass card variants you can drop straight into a Next.js or Vite project. The components handle the cross-browser -webkit-backdrop-filter fallback, the border treatment, and the dark/light mode props out of the box.

There's a curated list of what's available in best free glassmorphism components. The social feed card specifically is available with configurable blur radius, background opacity, and border width as component props — no custom CSS required.

What's the actual difference between hand-rolling it and using the component library? About 40 minutes of debugging Safari rendering bugs, three rounds of adjusting text contrast for WCAG AA compliance, and one very frustrating realization that backdrop-filter doesn't work on elements with overflow: hidden in WebKit. The library has already absorbed those hits.

The feed layout component — GlassFeed — also includes skeleton loading states. Each card slot shows an animated shimmer while data loads, using the same frosted glass base so the transition from loading to loaded doesn't cause a visual jank.

FAQ

Does backdrop-filter blur work in all modern browsers for a production social feed?

Yes, as of 2025 backdrop-filter has full support in Chrome, Firefox, Safari, and Edge. You still need the -webkit-backdrop-filter prefix for Safari on iOS. Firefox had limited support before version 103 but that's no longer a concern for any reasonable browser support policy.

Why does my glassmorphism card look opaque on mobile but transparent on desktop?

Most likely your background content has too little contrast or your rgba alpha is too high. On mobile screens with lower peak brightness, rgba(255,255,255,0.15) can appear nearly white. Try dropping to 0.08-0.10 and increase the gradient background saturation behind the cards.

Can I apply backdrop-filter blur to a card that also has overflow-hidden?

Not reliably in WebKit. Safari has a known bug where backdrop-filter stops working on elements with overflow: hidden. The fix is to wrap the content in an inner div with overflow-hidden and border-radius, and keep the backdrop-filter on the outer card element without overflow clipping.

What's a good blur radius value for glassmorphism social cards?

12px to 16px works well for desktop. Use 8px on mobile viewports under 640px for performance. Values above 20px start looking soft to the point of feeling unintentional rather than designed, and they cost more in GPU compositing.

How do I make the glass card border look right without it being too obvious?

Use border: 1px solid rgba(255,255,255,0.20) for dark backgrounds and border: 1px solid rgba(255,255,255,0.40) for the top and left edges to simulate a light source. A single uniform border around all four sides reads as an outlined box rather than a glass edge.

Will a glassmorphism feed pass WCAG contrast requirements?

Not automatically. Text on a semi-transparent background that sits over a gradient will have variable contrast depending on what portion of the gradient is behind each card. Test with real content using a contrast checker, and use text-white/90 or higher (not below /70) for body text on dark gradient backgrounds.

Free components in 40 styles
React & Tailwind, copy-paste ready.
Browse →

Read next

Glass Navigation Bar: Sticky Header with Backdrop BlurGlassmorphism Carousel: Slider Component with Frosted CardsImage Gallery with Lightbox: Accessible Photo Viewer in ReactLogin Form Variants: 6 Sign-In Designs with Validation