Next.js View Transitions ใช้ยังไง? พร้อมตัวอย่างจริง

🚀 ทำให้ Next.js ลื่นเหมือน Native App ด้วย View Transitions
หนึ่งใน pain หลักของเว็บยุคนี้คือ “การเปลี่ยนหน้าแล้วรู้สึกกระตุก” แม้จะใช้ SPA หรือ Next.js แล้ว UX ก็ยังไม่ smooth เท่ากับ native mobile app
ตอนนี้ Next.js + React มี feature ใหม่ที่ช่วยแก้ปัญหานี้ตรง ๆ คือ View Transitions API ✨
มันช่วยให้:
เปลี่ยนหน้าได้ลื่นขึ้น
ทำ shared element transition ได้ง่าย
ใช้แค่ CSS + React component
ไม่ต้องพึ่ง animation library หนัก ๆ
⚙️ เปิดใช้งาน View Transitions ใน Next.js
เริ่มจากเปิด experimental feature ใน
1next.config.ts
1import type { NextConfig } from 'next';23const nextConfig: NextConfig = {4experimental: {5viewTransition: true,6},7};89export default nextConfig;
⚠️ ตอนนี้ยังเป็น experimental แต่ใช้งานได้จริงแล้ว
🌍 ใช้งาน Route Transition แบบ Global
หลายคนเริ่มจากใส่ใน layout.tsx แต่จริง ๆ ถ้าต้องการให้ transition ทำงานถูกต้องกับ App Router ควรใช้ template.tsx
เพราะ:
layout.tsx จะ persist ระหว่าง route
template.tsx จะ remount ทุกครั้งที่เปลี่ยนหน้า
เหมาะกับ route transition มากกว่า
📌
1template.tsx
1import { ViewTransition } from 'react';23interface TemplateProps {4children: React.ReactNode;5}67const Template: React.FC<TemplateProps> = ({ children }) => {8return (9<ViewTransition default="page-transition">10{children}11</ViewTransition>12);13};1415export default Template;
🎨 สร้าง Animation ด้วย CSS
ใน
1globals.css
1::view-transition-group(.page-transition) {2animation-duration: 500ms;3}45::view-transition-old(.page-transition) {6animation: page-out 500ms ease;7}89::view-transition-new(.page-transition) {10animation: page-in 500ms ease;11}1213@keyframes page-out {14from {15opacity: 1;16transform: scale(1);17filter: blur(0);18}1920to {21opacity: 0;22transform: scale(0.98);23filter: blur(8px);24}25}2627@keyframes page-in {28from {29opacity: 0;30transform: scale(0.98);31filter: blur(8px);32}3334to {35opacity: 1;36transform: scale(1);37filter: blur(0);38}39}
👉 ทุกครั้งที่เปลี่ยน route จะรู้สึกลื่นขึ้นทันที เหมือน native app มากกว่าเดิมเยอะ
🧠 Shared Element Transition คือของจริง
จุดที่ View Transitions powerful จริง ๆ ไม่ใช่แค่ page fade
แต่มันคือ: “element เดียวกัน animate ข้ามหน้า”
เช่น:
blog card → blog detail
image thumbnail → hero image
title → expanded title
สิ่งนี้ทำให้ UX ดู premium มาก
🖼️ ตัวอย่าง: Blog Card → Blog Detail
📌 ฝั่ง Blog Card
1import { ViewTransition } from 'react';
1<ViewTransition2name={`blog-image-${item.fields.slug}`}3share="blog-image"4default="none"5>6<div className="blog-image-container w-full overflow-hidden">7{thumbnail && (8<Image9key={thumbnail.url}10src={thumbnail.url}11alt={item.fields.title}12width={thumbnail.width}13height={thumbnail.height}14className="blog-image max-h-[30vmin] object-cover group-hover/blog:scale-110 transition-transform duration-500"15/>16)}17</div>18</ViewTransition>
📌 ฝั่ง Blog Detail Page
1import { ViewTransition } from 'react';
1<ViewTransition2name={`blog-image-${blog.fields.slug}`}3share="blog-image"4default="none"5>6<Image7src={thumbnail?.url}8alt={blog.fields.title}9width={thumbnail?.width}10height={thumbnail?.height}11preload12sizes="(max-width: 1024px) 100vw, 1024px"13fetchPriority="high"14loading="eager"15className="blog-thumbnail object-cover"16/>17</ViewTransition>
📝 Shared Title Transition
นอกจาก image ยัง animate title ได้ด้วย
1import { ViewTransition } from 'react';
1<ViewTransition2name={`blog-title-${blog.fields.slug}`}3share="blog-title"4default="none"5>6<h17className="blog-title text-balance font-bold font-noto-sans-thai-looped text-3xl"8style={{9viewTransitionName: `blog-title-${blog.fields.slug}`,10viewTransitionClass: 'blog-title',11}}12>13{blog.fields.title}14</h1>15</ViewTransition>
👉 สิ่งสำคัญที่สุดคือ:
nameต้องตรงกันระหว่างสองหน้า
เช่น:
1name={`blog-image-${slug}`}
เพราะ browser จะใช้ชื่อนี้ในการจับคู่ element เดิมกับ element ใหม่
🎨 Animation สำหรับ Shared Element
1::view-transition-group(.blog-image) {2animation-duration: 700ms;3}45::view-transition-image-pair(.blog-image) {6overflow: hidden;7border-radius: 24px;8}910::view-transition-new(.blog-image) {11animation: scale-in 700ms ease;12}1314@keyframes scale-in {15from {16transform: scale(0.95);17opacity: 0;18}1920to {21transform: scale(1);22opacity: 1;23}24}
📌 จุดที่หลายคนไม่รู้คือ:
1::view-transition-image-pair()
ช่วยแก้ปัญหา image stretch และ clipping ระหว่าง morph animation ได้ดีมาก
⚡ Performance Tips (สำคัญมาก)
ถ้าอยากให้ smooth จริงแบบ production:
✅ ใช้
1loading="eager"
กับ element สำคัญ ✅ ใส่
1fetchPriority="high"
✅ preload image ✅ ลด layout shift ✅ ใช้ stable transition names ✅ อย่า animate element ใหญ่เกินจำเป็น
เพราะ View Transition ใช้ snapshot ของ DOM จริง ถ้า asset โหลดไม่ทันจะรู้สึกกระตุกทันที
🔥 Insight จากการใช้งานจริง
สิ่งที่หลายคนพลาด:
❌ ใช้แค่ page animation ❌ ไม่ทำ shared element transition ❌ transition name ไม่ unique ❌ image โหลดช้า ❌ ใส่ไว้ใน
1layout.tsx
แล้ว transition แปลก ๆ
ถ้าทำถูก:
👉 UX จะรู้สึกเหมือน native app ทันที โดยแทบไม่ต้องใช้ animation library เพิ่มเลย
🧩 สรุป
View Transitions เป็นหนึ่งใน feature ที่ impact สูงมากแต่ effort ต่ำ
เหมาะกับเว็บที่:
มี navigation บ่อย
image-heavy UI
blog
ecommerce
portfolio
dashboard
และที่สำคัญ: ใช้แค่ React + CSS + config นิดเดียวก็ได้ UX ที่ต่างจากเดิมชัดมาก 🚀
Latest Blogs
View all
Husky + lint-staged: ทำให้ Git Commit สะอาดขึ้นแบบอัตโนมัติ
ตั้งค่า Git workflow ให้สะอาดและอัตโนมัติด้วย Husky + lint-staged พร้อมสอนติดตั้ง วิธีใช้งาน ข้อดีข้อเสีย และ optional setup สำหรับ formatter/linter อย่าง Prettier และ Biome

ลาออกครั้งแรกโดยไม่มีงานใหม่: ประสบการณ์จริงของ Junior Developer
ประสบการณ์จริงของการลาออกครั้งแรกโดยยังไม่มีงานใหม่รองรับ จากเด็กจบสาย Graphic ที่ผันตัวมาเป็น Junior Developer ต้องเผชิญความไม่มั่นใจ การหางานสุดกดดัน และบทเรียนชีวิตที่ไม่มีในห้องเรียน

ตั้งค่า Rclone ใช้งานร่วมกับ Cloudflare R2 อย่างง่ายในไม่กี่ขั้นตอน
เรียนรู้วิธีเชื่อมต่อ Rclone กับ Cloudflare R2 (Object Storage แบบ S3-compatible) พร้อมตัวอย่างการตั้งค่า การสำรองข้อมูลอย่างง่ายในไม่กี่ขั้นตอน