Instagram Transition
Instagram-style transition where an image expands into a detail view
Instagram Transition
The Instagram transition is an Expansion Transition where a small image in a grid expands to full screen. Similar to the Pinterest transition, but the outgoing page remains static without animation, and only the incoming page animates.
Demo
Loading demo...
UX Principles
When to use?
The Instagram transition is used for transitions from image-centric feeds to detail views.
Suitability by Content Relationship
| 콘텐츠 관계 | 적합성 | 설명 |
|---|---|---|
| Unrelated content | ❌ | Not suitable as there's no connection point to expand from |
| Sibling relationship | ⚠️ | Could be considered for navigation between images in a grid |
| Hierarchical relationship | ✅ | Optimal for expanded views of the same content, like thumbnail→full view |
Key Use Cases
- Social media feeds: Transition from grid feed to post detail
- Photo galleries: Zoom from small thumbnails to large images
- Portfolios: From work grid to detail view
- Product listings: From product grid to detail page
Why does it work this way?
- Maintains focus: The outgoing page stays in place, making it clear where the user came from
- Performance optimization: Animating only one page ensures smooth transitions
- Natural expansion: The selected image naturally fills the screen
- Context preservation: The background remains visible, preventing disorientation
Motion Design
Expansion process (Gallery → Detail):
1. Gallery page remains static (no animation)
2. Detail page expands from the selected image position
3. Scales up to fill the screen with clip-path
4. Eventually occupies the entire screen
Contraction process (Detail → Gallery):
1. Detail page remains static (no animation)
2. Gallery page appears while contracting to target image position
3. Smoothly appears with opacity and scale animation
Basic Usage
1. Configure Transition
import { Ssgoi } from '@ssgoi/react';
import { instagram } from '@ssgoi/react/view-transitions';
const config = {
transitions: [
{
from: '/gallery',
to: '/gallery/*',
transition: instagram(),
symmetric: true
}
]
};
export default function App() {
return (
<Ssgoi config={config}>
{/* App content */}
</Ssgoi>
);
}
2. Mark Elements
Use different data attributes for gallery and detail pages:
// Gallery page (3-column grid)
function Gallery() {
return (
<div className="grid grid-cols-3 gap-1">
{items.map(item => (
<Link
key={item.id}
to={`/gallery/${item.id}`}
className="aspect-square"
>
<img
src={item.image}
alt={item.title}
data-instagram-gallery-key={item.id}
className="w-full h-full object-cover"
/>
</Link>
))}
</div>
);
}
// Detail page
function PostDetail({ item }) {
return (
<div className="min-h-screen">
<img
src={item.image}
alt={item.title}
data-instagram-detail-key={item.id}
className="w-full h-auto"
/>
<div className="p-4">
<p className="font-semibold">{item.likes} likes</p>
<p className="mt-2">{item.caption}</p>
</div>
</div>
);
}
Practical Examples
1. Instagram-Style Feed
Uniform 3-column grid:
// 3-column grid
function InstagramFeed() {
return (
<div className="grid grid-cols-3 gap-1">
{posts.map((post) => (
<div
key={post.id}
onClick={() => navigate(`/post/${post.id}`)}
className="relative aspect-square cursor-pointer group"
>
<img
src={post.image}
alt={post.title}
data-instagram-gallery-key={post.id}
className="w-full h-full object-cover"
/>
{/* Hover overlay */}
<div className="absolute inset-0 bg-black/40 opacity-0
group-hover:opacity-100 transition-opacity
flex items-center justify-center">
<span className="text-white font-semibold">
❤️ {post.likes.toLocaleString()}
</span>
</div>
</div>
))}
</div>
);
}
// Post detail
function PostView({ post }) {
return (
<div className="min-h-screen bg-black">
<img
src={post.image}
alt={post.title}
data-instagram-detail-key={post.id}
className="w-full h-auto"
/>
{/* Post details */}
<div className="bg-white p-4">
<div className="flex items-center gap-4 mb-4">
<button className="flex items-center gap-2">
<HeartIcon className="w-6 h-6" />
<span>{post.likes}</span>
</button>
<button>
<CommentIcon className="w-6 h-6" />
</button>
<button>
<ShareIcon className="w-6 h-6" />
</button>
</div>
<p className="text-sm">
<strong>{post.author}</strong> {post.caption}
</p>
<p className="text-xs text-gray-500 mt-2">
{post.timestamp}
</p>
</div>
</div>
);
}
2. Photo Portfolio
From work grid to fullscreen:
// Portfolio grid
function PortfolioGrid() {
return (
<main className="container mx-auto p-4">
<div className="grid grid-cols-3 gap-4">
{works.map((work) => (
<article
key={work.id}
onClick={() => navigate(`/work/${work.id}`)}
className="relative aspect-square rounded-lg overflow-hidden
cursor-pointer hover:scale-105 transition-transform"
>
<img
src={work.thumbnail}
alt={work.title}
data-instagram-gallery-key={work.id}
className="w-full h-full object-cover"
/>
<div className="absolute bottom-0 left-0 right-0 p-3
bg-gradient-to-t from-black/80 to-transparent">
<h3 className="text-white font-semibold text-sm">
{work.title}
</h3>
<p className="text-white/80 text-xs">{work.category}</p>
</div>
</article>
))}
</div>
</main>
);
}
// Work detail
function WorkDetail({ work }) {
return (
<div className="min-h-screen bg-gray-900">
<div className="max-w-4xl mx-auto">
<img
src={work.image}
alt={work.title}
data-instagram-detail-key={work.id}
className="w-full h-auto"
/>
<div className="p-6 text-white">
<h1 className="text-3xl font-bold mb-2">{work.title}</h1>
<p className="text-gray-400 mb-4">{work.category}</p>
<p className="text-gray-300 leading-relaxed">
{work.description}
</p>
<div className="mt-6 flex gap-4">
<span className="text-sm text-gray-400">
{work.date}
</span>
<span className="text-sm text-gray-400">
{work.client}
</span>
</div>
</div>
</div>
</div>
);
}
Customization
Spring Configuration
Adjust animation elasticity and speed:
instagram({
spring: {
stiffness: 150, // Lower for smoother animation
damping: 20 // Higher for faster settling
}
})
Precautions
Key Attribute Distinction
- Gallery:
data-instagram-gallery-key - Detail:
data-instagram-detail-key - Must use different attribute names
Layout Considerations
- Gallery should use uniform grid (3 columns recommended)
- Detail page needs sufficient padding
- Images should maintain aspect-ratio
Differences from Pinterest Transition
- Pinterest: Both pages animate
- Instagram: Only incoming page animates
- Performance: Instagram is lighter and faster
- Use case: Instagram is optimal for uniform grids
Accessibility
- Support ESC key to close detail page
- Enhanced keyboard navigation
- Instant transition when motion is reduced
Best Practices
✅ DO
- Use with uniform 3-column grids
- Apply to image-centric content
- Provide clear close button
- Use mobile-optimized layout
❌ DON'T
- Not suitable for irregular grids
- Avoid with text-heavy content
- Don't expand too many elements simultaneously
- Be cautious with large images on slow networks