วันเสาร์ที่ 5 เมษายน พ.ศ. 2568

การใช้ Image Optimization ใน Next.js: โหลดไว SEO ดี ประหยัดแบนด์วิธ!

เคยเจอปัญหาเว็บโหลดช้าเพราะรูปภาพขนาดใหญ่หรือเปล่า? 📷 ถ้าใช่ Next.js มีฟีเจอร์ Image Optimization ที่ช่วยให้รูปภาพโหลดเร็วขึ้น อัตโนมัติ ไม่ต้องไปใช้พวก TinyPNG หรือ Cloudinary เองเลย

🌟 ทำไมต้องใช้ Image Optimization ใน Next.js?

✅ ลดขนาดไฟล์รูปภาพ โดยอัตโนมัติ 📉
✅ รองรับการแปลงเป็น WebP ที่โหลดเร็วกว่า JPEG/PNG ⚡
✅ รองรับ Lazy Loading โหลดเฉพาะตอนที่รูปจะถูกแสดง 💤
✅ รองรับการปรับขนาดรูปแบบอัตโนมัติ ตามหน้าจอผู้ใช้ 📱
✅ ทำให้ SEO ดีขึ้น เพราะเว็บโหลดเร็ว 🚀

วันนี้เราจะมาเรียนรู้วิธีใช้ Next.js Image Optimization อย่างมือโปร 💪

📌 เริ่มต้นใช้งาน Image Optimization ใน Next.js

Next.js มีคอมโพเนนต์ <Image> ที่ช่วยจัดการรูปภาพอัตโนมัติ ให้ใช้แทน <img> ปกติ

📂 สร้างไฟล์ pages/index.js แล้วใส่โค้ดนี้

import Image from 'next/image';

export default function Home() {
  return (
    <div>
      <h1>🚀 ใช้ Next.js Image Optimization</h1>
      <Image
        src="/images/sample.jpg"
        alt="รูปตัวอย่าง"
        width={500}
        height={300}
      />
    </div>
  );
}

🔹 อธิบายโค้ด

  • <Image> – ใช้แทน <img>
  • src – พาธของรูปภาพ (ใช้ /public/images/sample.jpg)
  • alt – คำอธิบายรูป (สำคัญต่อ SEO)
  • width & height – กำหนดขนาดรูป (Next.js ปรับขนาดให้เหมาะสม)


🎯 เปรียบเทียบ <Image> กับ <img>

คุณสมบัติ <Image> (Next.js) <img> (HTML)
Lazy Loading ✅ อัตโนมัติ ❌ ต้องเพิ่ม loading="lazy" เอง
ปรับขนาดรูปอัตโนมัติ ✅ ❌
รองรับ WebP ✅ ❌
SEO Friendly ✅ ❌

💡 สรุป: ใช้ <Image> จะช่วยให้รูปภาพโหลดเร็วขึ้น โดยที่เราไม่ต้องทำอะไรเพิ่มเลย!


🖼 ใช้ Image Optimization กับรูปภาพจากภายนอก

ถ้ารูปภาพของคุณ อยู่ใน CDN หรือโดเมนอื่น ต้องเพิ่มโดเมนนั้นใน next.config.js

📂 แก้ไขไฟล์ next.config.js

module.exports = {
  images: {
    domains: ['example.com', 'cdn.example.com'],
  },
};

🔹 ตัวอย่างการใช้รูปจาก CDN

<Image
  src="https://example.com/image.jpg"
  width={500}
  height={300}
  alt="รูปจาก CDN"
/>

💡 ทำไมต้องเพิ่มโดเมนใน next.config.js?

Next.js ต้องการควบคุมความปลอดภัยและประสิทธิภาพ จึงไม่ให้โหลดรูปจากภายนอกโดยตรง ถ้าไม่ระบุไว้

📏 ปรับขนาดรูปภาพอัตโนมัติ

สามารถใช้ layout เพื่อให้รูปภาพปรับขนาดตามคอนเทนต์ได้

🔹 layout="responsive" (ขนาดเปลี่ยนตามหน้าจอ)

<Image
  src="/images/sample.jpg"
  alt="รูปตัวอย่าง"
  layout="responsive"
  width={16}
  height={9}
/>

📌 ภาพจะปรับขนาดตามสัดส่วน 16:9 โดยอัตโนมัติ

🔹 layout="intrinsic" (ขนาดเท่าต้นฉบับ)

<Image
  src="/images/sample.jpg"
  alt="รูปตัวอย่าง"
  layout="intrinsic"
  width={500}
  height={300}
/>

📌 ภาพจะใช้ขนาดจริงของไฟล์ และขยายตามหน้าจอ

🔹 layout="fill" (ให้รูปเต็มพื้นที่ของ container)

<div style={{ position: 'relative', width: '100%', height: '400px' }}>
  <Image
    src="/images/sample.jpg"
    alt="รูปตัวอย่าง"
    layout="fill"
    objectFit="cover"
  />
</div>

📌 รูปจะถูกยืดให้เต็ม container และ objectFit="cover" ป้องกันการเสียสัดส่วน

⚡ เปลี่ยนฟอร์แมตรูปเป็น WebP อัตโนมัติ

Next.js รองรับ WebP โดยไม่ต้องแปลงเอง!

<Image
  src="/images/sample.jpg"
  alt="รูปตัวอย่าง"
  width={500}
  height={300}
  quality={80}
/>

📌 Next.js จะสร้างรูปแบบ WebP อัตโนมัติถ้าผู้ใช้รองรับ


🔄 ใช้ Blur Effect สำหรับรูปโหลดช้า

Next.js มี blur effect ช่วยให้รูปที่โหลดช้า ไม่ทำให้หน้าเว็บดูแย่

<Image
  src="/images/sample.jpg"
  alt="รูปตัวอย่าง"
  width={500}
  height={300}
  placeholder="blur"
  blurDataURL="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA..."
/>

📌 ภาพจะมีเอฟเฟกต์เบลอจนกว่าจะโหลดเสร็จ

🔥 ใช้ Next.js Image API


Next.js มี API สำหรับ Resize และ Optimize รูป อัตโนมัติ

📌 ตัวอย่างการใช้ API

https://yourdomain.com/_next/image?url=/images/sample.jpg&w=500&q=75

🔹 w=500 → ปรับขนาดรูปเป็น 500px
🔹 q=75 → ลดคุณภาพให้เหลือ 75%

📌 สรุป

✅ ใช้ <Image> แทน <img> เพื่อให้เว็บโหลดเร็ว
✅ รองรับ Lazy Loading และ WebP อัตโนมัติ
✅ ปรับขนาดรูป ให้เหมาะสมกับทุกอุปกรณ์
✅ ใช้ layout ต่างๆ เพื่อให้รูปแสดงผลตามต้องการ
✅ ใช้ API ของ Next.js ช่วย Optimize รูปให้เบาและเร็ว

อยากให้เว็บโหลดเร็ว + SEO ดีขึ้น? ใช้ Next.js Image Optimization เลย! 🚀

วันศุกร์ที่ 4 เมษายน พ.ศ. 2568

วิธีเพิ่ม Meta Tags และทำ SEO ใน Next.js ให้เว็บติดอันดับ Google

ถ้าคุณทำเว็บด้วย Next.js แล้วอยากให้เว็บของคุณ ติดอันดับ Google หรือมี SEO (Search Engine Optimization) ที่ดีขึ้น Meta Tags เป็นสิ่งที่ต้องรู้!

Meta Tags คืออะไร? 🤔

Meta Tags คือ โค้ดที่ซ่อนอยู่ใน <head> ของเว็บเพจ ซึ่งช่วยให้ Google, Facebook, Twitter และแพลตฟอร์มอื่นๆ เข้าใจว่าเว็บของเราคืออะไร

ทำไม Meta Tags สำคัญ?

✅ ช่วยให้ Google จัดอันดับเว็บของคุณดีขึ้น (SEO)
✅ ช่วยให้ Social Media แสดงพรีวิวสวยๆ เวลาแชร์ลิงก์
✅ ช่วยให้เว็บโหลดเร็วขึ้น (Performance Boost)

วันนี้เราจะมาเรียนรู้ว่า Next.js ช่วยให้การเพิ่ม Meta Tags และการทำ SEO ง่ายขึ้นได้ยังไง 💡


🚀 เริ่มต้น: ใช้ <Head> ใน Next.js

ใน Next.js เราใช้คอมโพเนนต์ <Head> จาก next/head เพื่อใส่ Meta Tags ได้ง่ายๆ

🔹 ตัวอย่างใน pages/index.js

import Head from 'next/head';

export default function Home() {
  return (
    <>
      <Head>
        <title>สุดยอดเว็บ Next.js | ติดอันดับ Google ง่ายๆ 🚀</title>
        <meta name="description" content="เว็บ Next.js ที่สุดยอดสำหรับการทำ SEO และ Meta Tags" />
        <meta name="keywords" content="Next.js, SEO, Meta Tags, Google Ranking" />
        <meta name="author" content="Your Name" />
      </Head>
      <main>
        <h1>🔥 ยินดีต้อนรับสู่สุดยอดเว็บ Next.js!</h1>
      </main>
    </>
  );
}

🔹 อธิบายโค้ด

✅ <title> – เป็นชื่อของหน้าเว็บที่จะแสดงบนแท็บเบราว์เซอร์
✅ <meta name="description"> – บอก Google ว่าเว็บนี้เกี่ยวกับอะไร
✅ <meta name="keywords"> – เพิ่มคีย์เวิร์ดที่ช่วยให้ Google เข้าใจเว็บเรา (แต่ Google ให้ความสำคัญกับ description มากกว่า)
✅ <meta name="author"> – บอกว่าใครเป็นเจ้าของเว็บ


🎯 ปรับแต่ง Meta Tags ให้ Social Media

ถ้าเราแชร์ลิงก์บน Facebook, Twitter หรือ LINE แล้วอยากให้มี รูปภาพ, ชื่อเว็บ และคำอธิบายสวยๆ เราต้องเพิ่ม Open Graph (OG) Tags

🔹 ตัวอย่าง Open Graph Tags

<Head>
  <meta property="og:title" content="สุดยอดเว็บ Next.js | ติดอันดับ Google ง่ายๆ 🚀" />
  <meta property="og:description" content="เว็บ Next.js ที่สุดยอดสำหรับการทำ SEO และ Meta Tags" />
  <meta property="og:image" content="https://yourwebsite.com/og-image.jpg" />
  <meta property="og:url" content="https://yourwebsite.com/" />
  <meta property="og:type" content="website" />
</Head>

🔹 อธิบายโค้ด

✅ og:title – ชื่อของเว็บเมื่อแชร์บน Facebook
✅ og:description – คำอธิบายเว็บ
✅ og:image – รูปภาพพรีวิว (แนะนำขนาด 1200x630px)
✅ og:url – URL ของเว็บ
✅ og:type – ประเภทของเนื้อหา เช่น website, article


🐦 เพิ่ม Twitter Meta Tags

ถ้าอยากให้เว็บดูดีตอนแชร์บน Twitter ให้ใช้ Twitter Card Tags

<Head>
  <meta name="twitter:card" content="summary_large_image" />
  <meta name="twitter:title" content="สุดยอดเว็บ Next.js | ติดอันดับ Google ง่ายๆ 🚀" />
  <meta name="twitter:description" content="เว็บ Next.js ที่สุดยอดสำหรับการทำ SEO และ Meta Tags" />
  <meta name="twitter:image" content="https://yourwebsite.com/twitter-image.jpg" />
</Head>

🔹 อธิบายโค้ด

✅ twitter:card – summary_large_image ทำให้แสดงภาพใหญ่
✅ twitter:title – ชื่อของเว็บ
✅ twitter:description – คำอธิบาย
✅ twitter:image – รูปภาพที่ใช้แสดงบน Twitter


🏆 การทำ SEO ให้ Next.js ติดอันดับ Google

นอกจาก Meta Tags แล้ว Next.js ยังช่วยเรื่อง SEO ได้อีกหลายอย่าง เช่น SSR, SSG และ Dynamic Meta Tags

1️⃣ ใช้ Server-side Rendering (SSR) เพื่อโหลด Meta Tags แบบไดนามิก

บางหน้าเว็บ (เช่น บทความ หรือหน้าสินค้า) อาจต้องใช้ Meta Tags ที่เปลี่ยนแปลงตามข้อมูล Next.js รองรับ SSR เพื่อโหลด Meta Tags ตามเนื้อหาแต่ละหน้า

import Head from 'next/head';

export async function getServerSideProps() {
  const post = { title: "บทความสุดเจ๋ง!", description: "เนื้อหาดีมากเกี่ยวกับ SEO" };

  return { props: { post } };
}

export default function BlogPost({ post }) {
  return (
    <>
      <Head>
        <title>{post.title}</title>
        <meta name="description" content={post.description} />
      </Head>
      <main>
        <h1>{post.title}</h1>
        <p>{post.description}</p>
      </main>
    </>
  );
}

2️⃣ ใช้ Static Site Generation (SSG) ให้เว็บโหลดเร็ว

ถ้าเนื้อหาของเว็บเปลี่ยนไม่บ่อย เช่น หน้าเกี่ยวกับเรา หรือ หน้าผลิตภัณฑ์ เราสามารถใช้ SSG เพื่อให้เว็บโหลดเร็วขึ้น

export async function getStaticProps() {
  return {
    props: { title: "เกี่ยวกับเรา", description: "นี่คือหน้าเกี่ยวกับเรา" },
  };
}

🔍 เช็คว่า Meta Tags ทำงานหรือไม่?

หลังจากเพิ่ม Meta Tags แล้ว ต้องเช็คว่าใช้งานได้จริง โดยใช้เครื่องมือ

✅ Google Rich Results Test – https://search.google.com/test/rich-results
✅ Facebook Sharing Debugger – https://developers.facebook.com/tools/debug/
✅ Twitter Card Validator – https://cards-dev.twitter.com/validator


📌 สรุป

✅ ใช้ <Head> ใน Next.js เพื่อเพิ่ม Meta Tags
✅ เพิ่ม Open Graph Tags เพื่อให้ Social Media แสดงผลสวยๆ
✅ ใช้ Twitter Card Tags เพื่อให้เว็บดูดีเวลาทวีต
✅ ใช้ SSR และ SSG เพื่อปรับ Meta Tags ตามเนื้อหาแต่ละหน้า
✅ เช็คว่า Meta Tags ทำงานจริงผ่านเครื่องมือของ Google, Facebook และ Twitter

พร้อมทำ SEO ให้เว็บ Next.js ของคุณติดอันดับ Google แล้วหรือยัง? 🚀

วันพฤหัสบดีที่ 3 เมษายน พ.ศ. 2568

การใช้ Toast Notifications ใน Next.js: แจ้งเตือนแบบโปร ไม่ต้องรีโหลดหน้า

เคยเจอปัญหานี้ไหม? กดปุ่มแล้วไม่รู้ว่าระบบทำงานหรือยัง ส่งฟอร์มแล้วไม่แน่ใจว่าผ่านหรือไม่ หรืออยากให้เว็บมีแจ้งเตือนที่ดูดีแต่ไม่อยากเขียนเองให้ยุ่งยาก

วันนี้เราจะมาเรียนรู้วิธีใช้ Toast Notifications ใน Next.js แบบง่ายๆ โดยใช้ react-hot-toast ไลบรารีสุดเทพที่ช่วยให้แจ้งเตือนแบบลื่นไหล ดูดี และใช้งานง่าย

🔥 ทำไมต้องใช้ Toast Notifications?

Toast Notifications คือ ข้อความแจ้งเตือนแบบชั่วคราว ที่แสดงขึ้นมาในเว็บเมื่อมีเหตุการณ์บางอย่างเกิดขึ้น เช่น:

✅ แจ้งเตือนเมื่อทำรายการสำเร็จ ✅
✅ แจ้งเตือนข้อผิดพลาด (เช่น ฟอร์มไม่ถูกต้อง) ❌
✅ แจ้งเตือนระหว่างโหลดข้อมูล ⏳

Toast ดีๆ ควรเป็น Non-intrusive (ไม่รบกวน) และ Auto-dismiss (หายไปเอง) เพื่อไม่ให้ผู้ใช้รำคาญ

🚀 ติดตั้ง react-hot-toast ใน Next.js

เราจะใช้ react-hot-toast เพราะมันใช้ง่ายและทำงานได้ดีกับ Next.js

ติดตั้งแพ็กเกจ

npm install react-hot-toast

หรือถ้าใช้ Yarn

yarn add react-hot-toast


🎨 ตั้งค่า ToastProvider

ToastProvider เป็นคอมโพเนนต์ที่ช่วยให้เราสามารถใช้ toast ได้ทั่วแอป ต้องเพิ่มใน _app.js

แก้ไข pages/_app.js

import { Toaster } from 'react-hot-toast';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return (
    <>
      <Toaster position="top-right" reverseOrder={false} />
      <Component {...pageProps} />
    </>
  );
}

export default MyApp;

🔹 position="top-right" – ให้ toast แสดงที่มุมขวาบน
🔹 reverseOrder={false} – ให้ข้อความใหม่ขึ้นด้านล่าง


🎯 ใช้งาน toast แจ้งเตือน

react-hot-toast ใช้งานง่ายมาก มีฟังก์ชันหลักๆ เช่น

  • toast.success("ข้อความ") ✅ แจ้งเตือนสำเร็จ
  • toast.error("ข้อความ") ❌ แจ้งเตือนข้อผิดพลาด
  • toast.loading("กำลังโหลด...") ⏳ แจ้งเตือนระหว่างโหลด
  • toast.dismiss() 🚫 ลบ toast ที่กำลังแสดง


💡 ตัวอย่างการใช้งาน toast ในปุ่ม

สร้างไฟล์ pages/index.js และเพิ่มปุ่มที่กดแล้วแจ้งเตือน

import toast from 'react-hot-toast';

export default function Home() {
  const notifySuccess = () => toast.success('บันทึกข้อมูลสำเร็จ! 🎉');
  const notifyError = () => toast.error('เกิดข้อผิดพลาด ❌');
  const notifyLoading = () => {
    const toastId = toast.loading('กำลังโหลดข้อมูล...');
    setTimeout(() => toast.success('โหลดสำเร็จ ✅', { id: toastId }), 2000);
  };

  return (
    <div className="p-10">
      <h1 className="text-2xl font-bold mb-4">🔔 ทดลองใช้ Toast Notifications</h1>
      <button onClick={notifySuccess} className="bg-green-500 text-white px-4 py-2 mr-2 rounded">✅ แจ้งเตือนสำเร็จ</button>
      <button onClick={notifyError} className="bg-red-500 text-white px-4 py-2 mr-2 rounded">❌ แจ้งเตือนข้อผิดพลาด</button>
      <button onClick={notifyLoading} className="bg-blue-500 text-white px-4 py-2 rounded">⏳ แจ้งเตือนระหว่างโหลด</button>
    </div>
  );
}

🔍 อธิบายโค้ด:

  • toast.success() ใช้แจ้งเตือนเมื่อทำรายการสำเร็จ
  • toast.error() ใช้แจ้งเตือนเมื่อเกิดข้อผิดพลาด
  • toast.loading() ใช้แจ้งเตือนว่ากำลังโหลดข้อมูล และอัปเดตเป็น toast.success() หลังจากโหลดเสร็จ


🎨 ปรับแต่งสไตล์ Toast

เราสามารถ กำหนดสไตล์ ของ toast ได้ เช่น เปลี่ยนสีพื้นหลัง เปลี่ยนไอคอน หรือกำหนดเวลาหายไปเอง

1️⃣ กำหนดเวลาให้ toast หายไปอัตโนมัติ

toast.success('บันทึกเรียบร้อย!', {
  duration: 5000, // แสดงผล 5 วินาที
});

2️⃣ เปลี่ยนสีและไอคอน

toast('🛑 คำเตือน: โปรดตรวจสอบข้อมูล', {
  style: {
    border: '1px solid red',
    padding: '16px',
    color: 'red',
  },
});

3️⃣ กำหนด animation

toast.success('ทำสำเร็จ!', {
  style: { animation: 'bounce 1s ease-in-out' },
});


🔄 ใช้ Toast กับ API Requests

ลองใช้ toast กับ API จริงๆ เช่น fetch()

const fetchData = async () => {
  const toastId = toast.loading('กำลังโหลดข้อมูล...');

  try {
    const response = await fetch('/api/data');
    const result = await response.json();

    toast.success('โหลดข้อมูลสำเร็จ!', { id: toastId });
  } catch (error) {
    toast.error('โหลดข้อมูลล้มเหลว', { id: toastId });
  }
};

🔍 อธิบาย:

  • toast.loading() แจ้งเตือนว่ากำลังโหลด
  • ถ้าโหลดสำเร็จ → toast.success()
  • ถ้าเกิดข้อผิดพลาด → toast.error()


📌 ใช้ Toast กับฟอร์ม

ในฟอร์มเราสามารถใช้ toast แจ้งเตือนว่า กรอกข้อมูลถูกต้องหรือไม่

const handleSubmit = (e) => {
  e.preventDefault();
 
  if (!email) {
    toast.error('กรุณากรอกอีเมล!');
    return;
  }

  toast.success('ส่งข้อมูลสำเร็จ!');
};

🏆 สรุป

✅ react-hot-toast ใช้งานง่ายมาก
✅ ใช้กับ API Requests, ฟอร์ม และปุ่มต่างๆ ได้
✅ ปรับแต่งสไตล์และอนิเมชันได้
✅ ไม่ต้องรีโหลดหน้า ก็แจ้งเตือนได้แบบสวยๆ

พร้อมใช้ Toast แจ้งเตือนให้เว็บของคุณดูโปรขึ้นหรือยัง? 🚀

วันพุธที่ 2 เมษายน พ.ศ. 2568

การสร้าง Dashboard UI ใน Next.js: ทำง่าย ใช้งานจริง แบบโปรๆ

เคยไหม? เวลาจะสร้าง Dashboard สวยๆ ในเว็บแอปพลิเคชันแต่ไม่รู้จะเริ่มตรงไหน? วันนี้เราจะมาลุยกันว่า Next.js จะช่วยให้เราสร้าง Dashboard UI ที่สวยงาม ใช้งานได้จริง และมีประสิทธิภาพได้ยังไง

🔥 ทำไมต้องใช้ Next.js สำหรับ Dashboard?

Next.js เป็นหนึ่งใน React Framework ที่เหมาะกับการสร้าง Dashboard มากๆ เพราะมันมีฟีเจอร์ที่ช่วยให้การพัฒนาเร็วขึ้น เช่น

✅ Server-side Rendering (SSR) – โหลดข้อมูลแบบเร็วจี๋ 🚀
✅ Static Site Generation (SSG) – ลดโหลดเซิร์ฟเวอร์และเพิ่มความเร็ว
✅ API Routes – จัดการข้อมูลจาก Backend ได้ในตัว
✅ การรองรับ Tailwind CSS, MUI, Ant Design – ทำ UI ได้ง่ายขึ้น

🛠️ เริ่มต้นโปรเจกต์ Dashboard ด้วย Next.js

1️⃣ ติดตั้ง Next.js

ถ้ายังไม่มีโปรเจกต์ Next.js ให้สร้างใหม่ก่อน

npx create-next-app@latest my-dashboard
cd my-dashboard
npm install

หรือถ้าใช้ Yarn

yarn create next-app my-dashboard
cd my-dashboard
yarn install

2️⃣ ติดตั้ง UI Library (Tailwind CSS หรือ MUI)

เราจะใช้ Tailwind CSS เพราะมันเบาและทำให้ดีไซน์ง่าย

npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p

แล้วเพิ่ม Tailwind ลงในไฟล์ tailwind.config.js

module.exports = {
  content: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'],
  theme: {
    extend: {},
  },
  plugins: [],
};

จากนั้นใส่ Tailwind ลงใน styles/globals.css

@tailwind base;
@tailwind components;
@tailwind utilities;


🎨 สร้าง Layout หลักของ Dashboard

ปกติ Dashboard จะมี

✅ Sidebar
✅ Navbar
✅ Content Area

เราจะสร้าง Layout หลักที่ใช้ทั่วแอป

1️⃣ สร้างไฟล์ components/Layout.js

import Sidebar from './Sidebar';
import Navbar from './Navbar';

export default function Layout({ children }) {
  return (
    <div className="flex h-screen">
      <Sidebar />
      <div className="flex flex-col flex-1">
        <Navbar />
        <main className="p-4">{children}</main>
      </div>
    </div>
  );
}

2️⃣ สร้าง Sidebar (components/Sidebar.js)

import Link from 'next/link';

export default function Sidebar() {
  return (
    <div className="w-64 bg-gray-800 text-white h-screen p-5">
      <h2 className="text-xl font-bold mb-5">Dashboard</h2>
      <ul>
        <li className="mb-2">
          <Link href="/" className="block p-2 hover:bg-gray-700 rounded">🏠 หน้าหลัก</Link>
        </li>
        <li className="mb-2">
          <Link href="/analytics" className="block p-2 hover:bg-gray-700 rounded">📊 Analytics</Link>
        </li>
        <li className="mb-2">
          <Link href="/settings" className="block p-2 hover:bg-gray-700 rounded">⚙️ Settings</Link>
        </li>
      </ul>
    </div>
  );
}

3️⃣ สร้าง Navbar (components/Navbar.js)

export default function Navbar() {
  return (
    <div className="bg-white shadow-md p-4 flex justify-between">
      <h1 className="text-lg font-bold">Dashboard</h1>
      <div>👤 User</div>
    </div>
  );
}


4️⃣ ใช้ Layout ในหน้า pages/_app.js

import Layout from '../components/Layout';
import '../styles/globals.css';

function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}

export default MyApp;


📊 สร้างหน้า Dashboard พร้อมกราฟ

ใน Dashboard จริงๆ มักจะมี กราฟ และ ตารางข้อมูล เราจะใช้ Recharts เพื่อแสดงกราฟ

1️⃣ ติดตั้ง Recharts

npm install recharts


2️⃣ สร้างหน้า pages/index.js

import { LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';

const data = [
  { month: 'Jan', sales: 4000 },
  { month: 'Feb', sales: 3000 },
  { month: 'Mar', sales: 5000 },
  { month: 'Apr', sales: 7000 },
];

export default function Dashboard() {
  return (
    <div>
      <h1 className="text-2xl font-bold mb-4">📈 สถิติยอดขาย</h1>
      <div className="bg-white p-4 shadow-md">
        <ResponsiveContainer width="100%" height={300}>
          <LineChart data={data}>
            <XAxis dataKey="month" />
            <YAxis />
            <Tooltip />
            <Line type="monotone" dataKey="sales" stroke="#8884d8" strokeWidth={2} />
          </LineChart>
        </ResponsiveContainer>
      </div>
    </div>
  );
}


📌 เพิ่มตารางข้อมูล

เราจะใช้ TanStack Table เพื่อแสดงข้อมูลในรูปแบบตาราง

1️⃣ ติดตั้ง TanStack Table

npm install @tanstack/react-table

2️⃣ เพิ่มตารางใน pages/index.js

import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table';

const data = [
  { id: 1, name: 'John Doe', role: 'Admin' },
  { id: 2, name: 'Jane Smith', role: 'Editor' },
];

const columns = [
  { accessorKey: 'id', header: 'ID' },
  { accessorKey: 'name', header: 'ชื่อ' },
  { accessorKey: 'role', header: 'บทบาท' },
];

export default function Dashboard() {
  const table = useReactTable({ data, columns, getCoreRowModel: getCoreRowModel() });

  return (
    <div>
      <h1 className="text-2xl font-bold mb-4">👥 ผู้ใช้งาน</h1>
      <table className="border-collapse border border-gray-300 w-full">
        <thead>
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <th key={header.id} className="border p-2 bg-gray-100">
                  {flexRender(header.column.columnDef.header, header.getContext())}
                </th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map((row) => (
            <tr key={row.id}>
              {row.getVisibleCells().map((cell) => (
                <td key={cell.id} className="border p-2">
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

🎯 สรุป

✅ สร้าง Dashboard UI ใน Next.js ได้ง่าย
✅ ใช้ Tailwind CSS ทำให้ UI สวยแบบไม่ต้องเขียน CSS เอง
✅ ใช้ Recharts และ TanStack Table เพื่อแสดงข้อมูลได้อย่างมืออาชีพ

พร้อมสร้าง Dashboard ของคุณเองแล้วหรือยัง?

วันอังคารที่ 1 เมษายน พ.ศ. 2568

การแสดงผลตารางใน Next.js ด้วย TanStack Table: ง่าย สวย และยืดหยุ่นสุดๆ

ถ้าคุณเคยต้องทำตารางใน React หรือ Next.js แล้วรู้สึกว่ามันยุ่งยากไปหมด ไม่ว่าจะเป็นการจัดการข้อมูล การกรอง (filtering) การเรียงลำดับ (sorting) หรือการแบ่งหน้า (pagination) วันนี้เราจะมาแนะนำ TanStack Table (หรือชื่อเดิมคือ React Table) ที่จะช่วยให้คุณสร้างตารางขั้นเทพได้แบบง่ายๆ

ทำไมต้องใช้ TanStack Table?

ในโปรเจกต์ที่ต้องแสดงข้อมูลในรูปแบบตาราง เช่น รายการคำสั่งซื้อ, รายการผู้ใช้ หรือข้อมูลสถิติต่างๆ เราต้องการฟีเจอร์พวกนี้:

✅ การจัดการข้อมูลที่ยืดหยุ่น (เช่น ใช้ API ดึงข้อมูลมาแสดง)
✅ Sorting, Filtering, และ Pagination แบบไม่ต้องเขียนเอง
✅ รองรับการใช้งานกับ Next.js และ SSR (Server-side Rendering)

TanStack Table ทำได้หมด! 🎉

ติดตั้ง TanStack Table

ก่อนอื่น เราต้องติดตั้งไลบรารีก่อน

npm install @tanstack/react-table

หรือถ้าใช้ Yarn

yarn add @tanstack/react-table


ตัวอย่างการใช้งานพื้นฐาน

มาลองสร้างตารางง่ายๆ กันก่อน โดยใช้ข้อมูลแบบ static

1. ตั้งค่าข้อมูลและคอลัมน์

const data = [
  { id: 1, name: 'John Doe', age: 28, email: 'john@example.com' },
  { id: 2, name: 'Jane Smith', age: 32, email: 'jane@example.com' },
  { id: 3, name: 'Bob Brown', age: 24, email: 'bob@example.com' },
];

const columns = [
  { accessorKey: 'id', header: 'ID' },
  { accessorKey: 'name', header: 'ชื่อ' },
  { accessorKey: 'age', header: 'อายุ' },
  { accessorKey: 'email', header: 'อีเมล' },
];

อธิบาย:

  • data คือข้อมูลที่เราจะเอามาแสดง
  • columns กำหนดโครงสร้างของตาราง ใช้ accessorKey เพื่อระบุคีย์ของข้อมูล


2. ใช้ TanStack Table เพื่อแสดงตาราง

import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table';

export default function MyTable() {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    <table border="1">
      <thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th key={header.id}>{flexRender(header.column.columnDef.header, header.getContext())}</th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map((row) => (
          <tr key={row.id}>
            {row.getVisibleCells().map((cell) => (
              <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

สรุปโค้ด:

  • useReactTable() ใช้สร้างตารางจาก data และ columns
  • getCoreRowModel() ใช้เพื่อจัดการโครงสร้างของข้อมูล
  • flexRender() ช่วยให้เราสามารถเรนเดอร์ค่าที่กำหนดใน columns ได้


เพิ่ม Sorting (เรียงลำดับ)

ถ้าอยากให้ตารางสามารถเรียงลำดับ (sorting) ได้ ให้เพิ่ม getSortedRowModel()

import { useReactTable, getCoreRowModel, getSortedRowModel, flexRender } from '@tanstack/react-table';

export default function MyTable() {
  const [sorting, setSorting] = React.useState([]);
 
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    onSortingChange: setSorting,
    state: { sorting },
  });

  return (
    <table border="1">
      <thead>
        {table.getHeaderGroups().map((headerGroup) => (
          <tr key={headerGroup.id}>
            {headerGroup.headers.map((header) => (
              <th key={header.id} onClick={() => header.column.getToggleSortingHandler()?.()}>
                {flexRender(header.column.columnDef.header, header.getContext())}
                {header.column.getIsSorted() === 'asc' ? ' 🔼' : header.column.getIsSorted() === 'desc' ? ' 🔽' : ''}
              </th>
            ))}
          </tr>
        ))}
      </thead>
      <tbody>
        {table.getRowModel().rows.map((row) => (
          <tr key={row.id}>
            {row.getVisibleCells().map((cell) => (
              <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
            ))}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

ตอนนี้เราสามารถคลิกที่หัวตารางเพื่อเรียงข้อมูลได้แล้ว! 🎉


เพิ่ม Pagination (แบ่งหน้า)

ถ้าข้อมูลเยอะๆ เราอาจต้องการ pagination เพื่อให้ผู้ใช้สามารถเลื่อนดูหน้าอื่นๆ ได้

import { useReactTable, getCoreRowModel, getPaginationRowModel, flexRender } from '@tanstack/react-table';

export default function MyTable() {
  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
    getPaginationRowModel: getPaginationRowModel(),
  });

  return (
    <div>
      <table border="1">
        <thead>
          {table.getHeaderGroups().map((headerGroup) => (
            <tr key={headerGroup.id}>
              {headerGroup.headers.map((header) => (
                <th key={header.id}>{flexRender(header.column.columnDef.header, header.getContext())}</th>
              ))}
            </tr>
          ))}
        </thead>
        <tbody>
          {table.getRowModel().rows.map((row) => (
            <tr key={row.id}>
              {row.getVisibleCells().map((cell) => (
                <td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
      <div>
        <button onClick={() => table.previousPage()} disabled={!table.getCanPreviousPage()}>
          ก่อนหน้า
        </button>
        <span> หน้า {table.getState().pagination.pageIndex + 1} </span>
        <button onClick={() => table.nextPage()} disabled={!table.getCanNextPage()}>
          ถัดไป
        </button>
      </div>
    </div>
  );
}


สรุป

✅ TanStack Table ช่วยให้สร้างตารางใน Next.js ได้ง่ายและยืดหยุ่น
✅ รองรับ Sorting, Pagination, Filtering
✅ ทำงานได้ดีทั้ง Client-side และ Server-side Rendering

ถ้าคุณต้องการตารางที่เร็วและยืดหยุ่น TanStack Table คือตัวเลือกที่ดีสุด! 

วันจันทร์ที่ 31 มีนาคม พ.ศ. 2568

การจัดการฟอร์มใน Next.js ด้วย React Hook Form: ใช้ง่าย ประหยัดแรง

ถ้าพูดถึงการจัดการฟอร์มใน React หรือ Next.js หลายคนคงเคยเจอปัญหาปวดหัวกับการจัดการ state, validation, และการ submit ข้อมูลกันใช่ไหม? วันนี้เราจะมาทำความรู้จักกับ React Hook Form ซึ่งเป็นหนึ่งในไลบรารีจัดการฟอร์มที่ดีที่สุดสำหรับ React และ Next.js ทำให้ชีวิตนักพัฒนาง่ายขึ้นเยอะ!

ทำไมต้องใช้ React Hook Form?

โดยปกติ ถ้าเราจัดการฟอร์มใน React ด้วย useState() เราจะต้องสร้าง state แยกกันตามฟิลด์ของฟอร์ม เช่น

const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

แล้วทุกครั้งที่มีการเปลี่ยนค่าฟิลด์ เราต้องใช้ onChange มาจับข้อมูลอีก

<input type="text" value={name} onChange={(e) => setName(e.target.value)} />

มันใช้ได้ก็จริง แต่มันเยอะและยุ่งยาก!

React Hook Form (RHF) ช่วยลดความยุ่งยากนี้ ด้วยการทำให้ฟอร์มเป็นแบบ uncontrolled ซึ่งหมายความว่า ไม่ต้องใช้ useState() คุมค่าฟิลด์แบบ manual แต่ให้ React ทำงานร่วมกับ DOM โดยตรง

ติดตั้ง React Hook Form

ก่อนอื่น ต้องติดตั้ง React Hook Form ก่อน

npm install react-hook-form

หรือถ้าใช้ Yarn

yarn add react-hook-form


ใช้ React Hook Form ใน Next.js

มาดูโค้ดตัวอย่างง่ายๆ ของการใช้งาน React Hook Form ใน Next.js

สร้างฟอร์มแบบง่าย

import { useForm } from 'react-hook-form';

export default function MyForm() {
  const { register, handleSubmit } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} placeholder="ชื่อ" />
      <input {...register('email')} type="email" placeholder="อีเมล" />
      <button type="submit">ส่ง</button>
    </form>
  );
}

อธิบายโค้ด:

  • useForm() คือฟังก์ชันหลักที่ใช้ดึง API ต่างๆ
  • register() ใช้สำหรับเชื่อมโยงอินพุตฟอร์มกับ React Hook Form
  • handleSubmit(onSubmit) ใช้จัดการการส่งฟอร์ม
  • data ที่ได้จาก onSubmit() จะเป็นอ็อบเจ็กต์ที่รวมค่าทั้งหมดในฟอร์ม


การเพิ่ม Validation

ถ้าไม่อยากให้ผู้ใช้กรอกมั่วๆ ก็ต้องเพิ่ม Validation Rules เข้าไป โดยใช้ register()

import { useForm } from 'react-hook-form';

export default function MyForm() {
  const {
    register,
    handleSubmit,
    formState: { errors },
  } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name', { required: 'ต้องกรอกชื่อ' })} placeholder="ชื่อ" />
      {errors.name && <p>{errors.name.message}</p>}

      <input {...register('email', { required: 'ต้องกรอกอีเมล', pattern: { value: /\S+@\S+\.\S+/, message: 'รูปแบบอีเมลไม่ถูกต้อง' } })} placeholder="อีเมล" />
      {errors.email && <p>{errors.email.message}</p>}

      <button type="submit">ส่ง</button>
    </form>
  );
}

สิ่งที่เพิ่มเข้ามา:

  • ใช้ errors เพื่อตรวจจับข้อผิดพลาดในแต่ละฟิลด์
  • ถ้ามี error จะแสดงข้อความแจ้งเตือนใต้ input


การใช้ Default Values

บางครั้งเราอาจต้องกำหนดค่าตั้งต้นให้ฟอร์ม เช่น กรณีแก้ไขข้อมูลผู้ใช้

const { register, handleSubmit } = useForm({
  defaultValues: {
    name: 'John Doe',
    email: 'john@example.com',
  },
});


ใช้ Controller กับ React Hook Form

ถ้าเราต้องใช้ React Component ของบุคคลที่สาม (เช่น React Select หรือ DatePicker) ที่ไม่รองรับ register() โดยตรง เราต้องใช้ Controller

import { useForm, Controller } from 'react-hook-form';
import Select from 'react-select';

export default function MyForm() {
  const { control, handleSubmit } = useForm();

  const onSubmit = (data) => {
    console.log(data);
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Controller
        name="gender"
        control={control}
        render={({ field }) => <Select {...field} options={[{ label: 'ชาย', value: 'male' }, { label: 'หญิง', value: 'female' }]} />}
      />
      <button type="submit">ส่ง</button>
    </form>
  );
}

ใช้งานร่วมกับ Next.js API Routes

ถ้าเราต้องการส่งข้อมูลไปที่ API ใน Next.js สามารถทำแบบนี้

const onSubmit = async (data) => {
  const response = await fetch('/api/submit', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(data),
  });
  const result = await response.json();
  console.log(result);
};

ฝั่ง API Route (/pages/api/submit.js)

export default function handler(req, res) {
  if (req.method === 'POST') {
    res.status(200).json({ message: 'ฟอร์มถูกส่งแล้ว!', data: req.body });
  } else {
    res.status(405).json({ message: 'Method Not Allowed' });
  }
}

สรุป

React Hook Form เป็นตัวช่วยที่ทำให้การจัดการฟอร์มใน Next.js ง่ายขึ้น ลดโค้ดที่ต้องเขียน และรองรับ validation แบบ built-in

  • ใช้งานง่าย – ใช้ register() เชื่อมโยงฟิลด์ได้เลย
  • ไม่ต้องใช้ useState() แยกฟิลด์ – ประหยัดโค้ด
  • รองรับ validation ในตัว – กำหนดเงื่อนไขได้ง่าย
  • ทำงานเร็วกว่าโซลูชันอื่นๆ เช่น Formik

ถ้าคุณต้องการจัดการฟอร์มใน Next.js React Hook Form คือคำตอบ!

วันอาทิตย์ที่ 30 มีนาคม พ.ศ. 2568

การใช้ Zustand หรือ Redux ใน Next.js: อะไรดีกว่ากัน?

ถ้าพูดถึงการจัดการ state ใน Next.js เชื่อว่าหลายคนต้องเคยลังเลระหว่าง Zustand กับ Redux แน่นอน เพราะทั้งสองตัวนี้ได้รับความนิยมมากและมีแนวทางการทำงานที่แตกต่างกัน วันนี้เราจะมาคุยกันแบบบ้านๆ ไม่เน้นทฤษฎีเยอะ แต่เน้นให้เข้าใจง่าย ว่าควรเลือกใช้ตัวไหนดีสำหรับโปรเจกต์ของคุณ

ทำไมต้องจัดการ State ใน Next.js?

Next.js เป็น React Framework ที่ช่วยให้เราสร้างเว็บแอปพลิเคชันได้เร็วและดีขึ้น แต่ถึงจะเทพแค่ไหน Next.js ก็ยังมีปัญหาเรื่องการจัดการ state ที่เราต้องคิดหนัก โดยเฉพาะเมื่อเว็บของเรามีหลายหน้า (multi-page) หรือมีข้อมูลที่แชร์กันระหว่างคอมโพเนนต์หลายตัว

ในกรณีที่ state ของเราซับซ้อนขึ้น การใช้ useState() หรือ useContext() อาจจะไม่พอแล้ว เช่น

  • มีหลายคอมโพเนนต์ต้องใช้ข้อมูลร่วมกัน
  • ต้องเก็บสถานะของผู้ใช้ที่ล็อกอิน
  • ต้องดึงข้อมูลจาก API แล้วแชร์ให้ทุกหน้าใช้

ถึงจุดนี้แหละ ที่พวก State Management Library อย่าง Zustand และ Redux โผล่มาช่วยชีวิตเรา

Redux: เจ้าพ่อ State Management

จุดเด่นของ Redux

Redux เป็น state container ระดับตำนานที่อยู่กับ React มานาน มันใช้ single source of truth (เก็บ state ไว้ที่เดียว) และเปลี่ยนค่า state ด้วย action และ reducer

สิ่งที่ทำให้ Redux เจ๋งคือ

  • Predictable State – ทุกการเปลี่ยนแปลงของ state ถูกควบคุมโดย action และ reducer ทำให้ debug ง่าย
  • DevTools สุดเทพ – มี Redux DevTools ให้ตรวจสอบทุกการเปลี่ยนแปลงของ state ได้แบบ real-time
  • Ecosystem ใหญ่โต – มี middleware อย่าง Redux Thunk หรือ Redux Saga ช่วยให้จัดการ asynchronous action ได้ง่ายขึ้น


จุดด้อยของ Redux

  • โค้ดยาว – Redux ต้องสร้าง action, reducer และ dispatch ซึ่งทำให้โค้ดยืดยาว ถ้าทำผิดที่เดียวอาจล้มทั้งระบบ
  • ต้องใช้ boilerplate เยอะ – แค่จะอัพเดท state ต้องผ่านหลายขั้นตอน เช่น สร้าง action → dispatch action → reducer → update store
  • Learning Curve สูง – สำหรับมือใหม่ Redux อาจทำให้ปวดหัว เพราะต้องเข้าใจโครงสร้างหลายอย่าง



Zustand: เบา สั้น และเร็ว

จุดเด่นของ Zustand

Zustand เป็น state management ที่เบาและง่ายกว่า Redux มันใช้แนวคิดของ flux pattern แต่ไม่ต้องมี action หรือ reducer ให้วุ่นวาย

สิ่งที่ทำให้ Zustand น่าสนใจคือ

  • API ใช้ง่าย – ไม่ต้องมี action หรือ reducer แค่สร้าง store แล้วใช้ได้เลย
  • ไม่มี boilerplate – Zustand ใช้ฟังก์ชันเดียวสร้าง state ได้ทันที
  • ใช้งานร่วมกับ Server Components ได้ดี – ทำให้เหมาะกับ Next.js ที่ใช้ App Router


จุดด้อยของ Zustand

  • ไม่มี DevTools ที่เทพเท่า Redux – ถ้าต้องการ debug อาจต้องใช้ console.log แทน
  • เหมาะกับโปรเจกต์ขนาดกลาง-เล็ก – ถ้าเป็นแอปขนาดใหญ่ Redux อาจจะควบคุม state ได้ดีกว่า





เปรียบเทียบ Redux vs Zustand

Feature Redux Zustand
โครงสร้างโค้ด ซับซ้อน ต้องมี action, reducer, store เรียบง่าย ใช้ฟังก์ชันเดียวสร้าง store
Performance อาจมี overhead สูงถ้าใช้ไม่ดี เบาและเร็วกว่า Redux
DevTools Redux DevTools ช่วย debug ได้ดี มี DevTools แต่ไม่เทพเท่า Redux
การใช้งานกับ Next.js ต้อง config เพิ่ม ใช้งานง่าย ไม่มี config ยุ่งยาก
เหมาะกับโปรเจกต์ ใหญ่และซับซ้อน เล็ก-กลาง


ควรใช้ Zustand หรือ Redux ใน Next.js?

ใช้ Redux ถ้า

✅ โปรเจกต์ของคุณ ซับซ้อน และต้องจัดการ state เยอะ
✅ ต้องการ DevTools เพื่อ debug state ได้ง่าย
✅ แอปของคุณต้องการ middleware เช่น Redux Thunk หรือ Redux Saga

ใช้ Zustand ถ้า

✅ คุณต้องการโค้ดที่ เรียบง่าย และ ไม่ต้องการ boilerplate
✅ โปรเจกต์ของคุณเป็น ขนาดเล็ก-กลาง และไม่ต้องการ state management ที่หนัก
✅ คุณใช้ Next.js App Router และต้องการ state management ที่เข้ากันได้ดี


ตัวอย่างการใช้ Redux กับ Next.js

ก่อนจะใช้ Redux กับ Next.js ต้องติดตั้งแพ็กเกจก่อน

npm install @reduxjs/toolkit react-redux

จากนั้นสร้าง store.js

import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1; },
    decrement: (state) => { state.value -= 1; },
  },
});

export const { increment, decrement } = counterSlice.actions;

export const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
  },
});

แล้วใช้ Provider ใน _app.js หรือ layout.js

import { Provider } from 'react-redux';
import { store } from '../store';

export default function App({ Component, pageProps }) {
  return (
    <Provider store={store}>
      <Component {...pageProps} />
    </Provider>
  );
}

ดึงค่า state ไปใช้ในคอมโพเนนต์

import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from '../store';

export default function Counter() {
  const count = useSelector((state) => state.counter.value);
  const dispatch = useDispatch();

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => dispatch(increment())}>+</button>
      <button onClick={() => dispatch(decrement())}>-</button>
    </div>
  );
}

ตัวอย่างการใช้ Zustand กับ Next.js

ติดตั้ง Zustand ก่อน

npm install zustand

สร้าง store.js

import { create } from 'zustand';

const useCounterStore = create((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
  decrement: () => set((state) => ({ count: state.count - 1 })),
}));

export default useCounterStore;

ใช้ในคอมโพเนนต์

import useCounterStore from '../store';

export default function Counter() {
  const { count, increment, decrement } = useCounterStore();

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={increment}>+</button>
      <button onClick={decrement}>-</button>
    </div>
  );
}

สรุป

  • Redux เหมาะกับโปรเจกต์ขนาดใหญ่ที่ต้องการความเป็นระบบ
  • Zustand เหมาะกับโปรเจกต์ที่ต้องการความง่ายและเร็ว
  • ถ้าคุณไม่แน่ใจ ให้ลองใช้ Zustand ก่อน ถ้ารู้สึกว่าไม่พอ ค่อยย้ายไป Redux

สุดท้ายแล้ว การเลือกเครื่องมือขึ้นอยู่กับ ความเหมาะสมของโปรเจกต์ ไม่มีกฎตายตัวว่าต้องใช้ตัวไหนเสมอไป

วันเสาร์ที่ 29 มีนาคม พ.ศ. 2568

🚀 การใช้ Headless UI กับ Next.js: ยืดหยุ่นกว่า สวยกว่า ใช้งานง่าย!

อยากได้ UI สวยๆ แต่ไม่อยากติดดีไซน์เดิมๆ ใช่ไหม? 🤔
Headless UI คือทางออก! มันเป็นชุดคอมโพเนนต์ที่ ไม่มีสไตล์ตายตัว ให้คุณออกแบบเองได้เต็มที่

ในบทความนี้ เราจะมาดูว่า Headless UI คืออะไร ทำไมถึงเหมาะกับ Next.js และมีวิธีใช้งานยังไงบ้าง พร้อมตัวอย่างโค้ดที่ใช้ได้จริง! 🎯

🎭 Headless UI คืออะไร?

Headless UI เป็นชุดของ UI Components ที่พัฒนาโดยทีม Tailwind CSS ซึ่งเน้นให้ ความยืดหยุ่น และ ปรับแต่งสไตล์เองได้

🔹 ข้อแตกต่างจาก UI Library ทั่วไป

Headless UI UI Library ทั่วไป (เช่น MUI, Ant Design)
ดีไซน์ ไม่มีดีไซน์บังคับ ปรับแต่งเอง มีดีไซน์ตายตัว ต้องใช้ตามที่ Library กำหนด
ความยืดหยุ่น ใช้กับ Tailwind, CSS, Styled Components ได้หมด ต้องใช้ตามโครงสร้างที่ Library กำหนด
ประสิทธิภาพ เบา เร็ว ไม่โหลดสไตล์เกินจำเป็น อาจมี CSS หรือ JS ส่วนเกินที่ไม่ได้ใช้
การรองรับ Accessibility มีการจัดการโฟกัสและคีย์บอร์ดให้ บางตัวรองรับ แต่บางตัวอาจต้องทำเพิ่มเอง
เหมาะกับ คนที่ต้องการ Custom UI เอง คนที่ต้องการ UI พร้อมใช้

🛠 วิธีติดตั้ง Headless UI ใน Next.js

ติดตั้งผ่าน npm หรือ yarn ง่ายๆ

npm install @headlessui/react

หรือ

yarn add @headlessui/react


🎯 ตัวอย่างการใช้งาน Headless UI

🔥 1️⃣ ทำ Modal ที่ใช้จริงได้เลย

Modal หรือ Dialog เป็นสิ่งที่ใช้บ่อยในเว็บ เช่น แจ้งเตือน, ฟอร์มสมัครสมาชิก


🔹 ตัวอย่าง Modal แบบใช้ Headless UI

import { Dialog, Transition } from "@headlessui/react";
import { Fragment, useState } from "react";

export default function ExampleModal() {
  const [isOpen, setIsOpen] = useState(false);

  return (
    <div className="text-center mt-10">
      <button
        onClick={() => setIsOpen(true)}
        className="px-4 py-2 bg-blue-500 text-white rounded"
      >
        เปิด Modal
      </button>

      <Transition appear show={isOpen} as={Fragment}>
        <Dialog as="div" className="fixed inset-0 z-50 flex items-center justify-center" onClose={() => setIsOpen(false)}>
          <div className="bg-white p-6 rounded-lg shadow-lg">
            <Dialog.Title className="text-lg font-bold">นี่คือ Modal!</Dialog.Title>
            <Dialog.Description className="mt-2 text-gray-600">
              กดปุ่มปิดเพื่อออกจาก Modal
            </Dialog.Description>
            <button
              onClick={() => setIsOpen(false)}
              className="mt-4 px-4 py-2 bg-red-500 text-white rounded"
            >
              ปิด
            </button>
          </div>
        </Dialog>
      </Transition>
    </div>
  );
}

✅ ทำไม Headless UI ถึงดีกว่า Modal ปกติ?

  • ใช้งานง่าย แค่ใส่ Dialog
  • รองรับ Keyboard Accessibility อัตโนมัติ
  • ไม่มีสไตล์เกินจำเป็น 👉 Customize ได้เต็มที่!


📜 2️⃣ ทำ Dropdown Menu แบบโปร

🔹 ตัวอย่าง Dropdown Menu

import { Menu, Transition } from "@headlessui/react";
import { Fragment } from "react";

export default function Dropdown() {
  return (
    <Menu as="div" className="relative inline-block text-left">
      <Menu.Button className="px-4 py-2 bg-blue-500 text-white rounded">เมนู</Menu.Button>

      <Transition
        as={Fragment}
        enter="transition ease-out duration-100"
        enterFrom="opacity-0 scale-95"
        enterTo="opacity-100 scale-100"
        leave="transition ease-in duration-75"
        leaveFrom="opacity-100 scale-100"
        leaveTo="opacity-0 scale-95"
      >
        <Menu.Items className="absolute mt-2 w-48 bg-white border rounded shadow-lg">
          <Menu.Item>
            {({ active }) => (
              <a className={`block px-4 py-2 ${active ? "bg-gray-200" : ""}`} href="#">
                โปรไฟล์
              </a>
            )}
          </Menu.Item>
          <Menu.Item>
            {({ active }) => (
              <a className={`block px-4 py-2 ${active ? "bg-gray-200" : ""}`} href="#">
                ตั้งค่า
              </a>
            )}
          </Menu.Item>
        </Menu.Items>
      </Transition>
    </Menu>
  );
}

✅ Dropdown ของ Headless UI ดียังไง?

  • ปรับแต่งได้ง่าย ไม่มีสไตล์บังคับ
  • รองรับ Keyboard Navigation
  • Animate ได้ Smooth (แค่ใช้ Transition)


🏁 สรุป

หัวข้อ รายละเอียด
Headless UI คืออะไร? ชุดคอมโพเนนต์ที่ไม่มีสไตล์บังคับ ให้เราปรับแต่งได้เอง
ข้อดี ยืดหยุ่น, ใช้กับ Tailwind CSS ได้ดี, เร็วและเบากว่า UI Library อื่นๆ
การติดตั้ง npm install @headlessui/react
คอมโพเนนต์ที่ใช้งานบ่อย Modal (Dialog), Dropdown (Menu), Tabs, Popover
เหมาะกับใคร? คนที่ต้องการ UI ที่ Custom ได้เต็มที่ โดยไม่อยากติดดีไซน์เดิมๆ

🎯 สรุปง่ายๆ

ถ้าคุณอยากให้เว็บมี UI ที่ยืดหยุ่น ไม่ถูกบังคับด้วยดีไซน์เดิมๆ และรองรับ Accessibility ดีเยี่ยม 👉 ใช้ Headless UI เถอะ!

ลองไปใช้กับ Tailwind CSS แล้วคุณจะหลงรัก 💙

วันศุกร์ที่ 28 มีนาคม พ.ศ. 2568

🌙 การทำ Dark Mode ใน Next.js: ให้เว็บของคุณเท่ขึ้นแบบมืออาชีพ!

เบื่อไหม? กับเว็บที่สว่างจ้าจนตาล้า 😵‍💫 ถ้าคุณอยากให้ผู้ใช้เลือกได้ว่าอยากใช้ โหมดสว่าง (Light Mode) หรือ โหมดมืด (Dark Mode) ใน Next.js เรามีวิธีง่ายๆ ให้คุณลองทำ!

ในบทความนี้ เราจะมาเจาะลึก การทำ Dark Mode ใน Next.js แบบครบทุกวิธี ทั้งใช้ CSS Variables, useState, localStorage และไลบรารีเสริม เช่น Next Themes กับ Tailwind CSS พร้อมตัวอย่างโค้ดที่เอาไปใช้ได้เลย! 🚀


🔥 ทำไมต้องมี Dark Mode?

✅ ข้อดีของ Dark Mode

  • ช่วยลดอาการล้าสายตา 👀 (สำหรับคนที่ใช้เว็บตอนกลางคืน)
  • ประหยัดแบตเตอรี่ 🔋 (โดยเฉพาะจอ OLED)
  • ดูเท่! 😎 หลายเว็บใหญ่ๆ ก็ใช้ เช่น YouTube, Twitter


🛠 วิธีทำ Dark Mode ใน Next.js

🚀 1. ใช้ CSS Variables + useState

วิธีนี้เหมาะกับ:
✅ เว็บที่ไม่ซับซ้อนมาก
✅ ไม่ต้องใช้ไลบรารีเสริม


👨‍💻 ขั้นตอนการทำ

1️⃣ สร้าง globals.css และกำหนด Theme

แก้ไขไฟล์ styles/globals.css

:root {
  --bg-color: #ffffff;
  --text-color: #000000;
}

.dark-mode {
  --bg-color: #121212;
  --text-color: #ffffff;
}

body {
  background-color: var(--bg-color);
  color: var(--text-color);
  transition: background 0.3s, color 0.3s;
}

2️⃣ เพิ่มปุ่มสลับโหมดใน index.js

เปิดไฟล์ pages/index.js แล้วเพิ่มโค้ดนี้

import { useState } from "react";

export default function Home() {
  const [darkMode, setDarkMode] = useState(false);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    document.body.classList.toggle("dark-mode");
  };

  return (
    <div style={{ textAlign: "center", marginTop: "50px" }}>
      <h1>Dark Mode in Next.js 🌙</h1>
      <button
        onClick={toggleDarkMode}
        style={{
          padding: "10px 20px",
          fontSize: "18px",
          cursor: "pointer",
          background: darkMode ? "#fff" : "#333",
          color: darkMode ? "#000" : "#fff",
          border: "none",
          borderRadius: "5px",
        }}
      >
        {darkMode ? "Switch to Light Mode ☀️" : "Switch to Dark Mode 🌙"}
      </button>
    </div>
  );
}

🛠 2. ใช้ localStorage จำค่าโหมด

ปัญหา: ถ้ารีเฟรชหน้าเว็บ ค่าโหมดที่เลือกไว้จะหายไป
ทางแก้: ใช้ localStorage จำค่าการตั้งค่าของผู้ใช้

อัปเดตโค้ด index.js

import { useState, useEffect } from "react";

export default function Home() {
  const [darkMode, setDarkMode] = useState(false);

  useEffect(() => {
    const savedTheme = localStorage.getItem("theme");
    if (savedTheme === "dark") {
      setDarkMode(true);
      document.body.classList.add("dark-mode");
    }
  }, []);

  const toggleDarkMode = () => {
    setDarkMode(!darkMode);
    if (!darkMode) {
      document.body.classList.add("dark-mode");
      localStorage.setItem("theme", "dark");
    } else {
      document.body.classList.remove("dark-mode");
      localStorage.setItem("theme", "light");
    }
  };

  return (
    <div style={{ textAlign: "center", marginTop: "50px" }}>
      <h1>Dark Mode in Next.js 🌙</h1>
      <button
        onClick={toggleDarkMode}
        style={{
          padding: "10px 20px",
          fontSize: "18px",
          cursor: "pointer",
          background: darkMode ? "#fff" : "#333",
          color: darkMode ? "#000" : "#fff",
          border: "none",
          borderRadius: "5px",
        }}
      >
        {darkMode ? "Switch to Light Mode ☀️" : "Switch to Dark Mode 🌙"}
      </button>
    </div>
  );
}

✅ ข้อดี:

  • จำค่าธีมได้หลังจากรีเฟรช


🎨 3. ใช้ next-themes (แนะนำ!)

✅ ข้อดีของ next-themes

  • ใช้ง่ายมาก (ไม่ต้องเขียนโค้ดเยอะ)
  • รองรับ ระบบ Dark Mode อัตโนมัติจาก OS



🛠 ติดตั้ง next-themes

npm install next-themes


👨‍💻 ใช้งาน next-themes

1️⃣ แก้ไข _app.js

import { ThemeProvider } from "next-themes";

export default function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider attribute="class">
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

2️⃣ แก้ไข index.js

import { useTheme } from "next-themes";
import { useEffect, useState } from "react";

export default function Home() {
  const { theme, setTheme } = useTheme();
  const [mounted, setMounted] = useState(false);

  useEffect(() => setMounted(true), []);

  if (!mounted) return null; // ป้องกัน Hydration Error

  return (
    <div className="p-5 text-center">
      <h1 className="text-2xl">Dark Mode with next-themes 🌙</h1>
      <button
        onClick={() => setTheme(theme === "dark" ? "light" : "dark")}
        className="mt-3 px-4 py-2 text-white bg-black rounded"
      >
        {theme === "dark" ? "Switch to Light Mode ☀️" : "Switch to Dark Mode 🌙"}
      </button>
    </div>
  );
}

🔥 ง่ายสุดๆ แค่เรียก setTheme() แล้วปล่อยให้ next-themes จัดการให้!

🏁 สรุป

วิธี ข้อดี ข้อเสีย
CSS Variables + useState ง่าย, ไม่ต้องใช้ไลบรารี รีเฟรชแล้วค่าหาย
localStorage จำค่าธีมได้หลังรีเฟรช ต้องใช้ useEffect
next-themes (แนะนำ) รองรับ Dark Mode อัตโนมัติ ต้องติดตั้งไลบรารี

🎯 ถ้าคุณอยากทำ Dark Mode แบบง่ายสุด 👉 ใช้ next-themes
🎯 ถ้าอยากควบคุมเองทั้งหมด 👉 ใช้ CSS Variables + localStorage

🚀 ลองทำเลย แล้วเว็บของคุณจะดูโปรขึ้น 300%!! 😆

วันพฤหัสบดีที่ 27 มีนาคม พ.ศ. 2568

การใช้ Framer Motion กับ Next.js: เพิ่ม Animation ให้เว็บดูโปรแบบมืออาชีพ 🚀

อย่าปล่อยให้เว็บนิ่งเหมือน PowerPoint สไลด์แรก 😆 ถ้าอยากให้เว็บของคุณดู Smooth และมีชีวิตชีวา Framer Motion คือทางออก!

ในบทความนี้ เราจะมาดูกันว่า Framer Motion ทำงานยังไง และเราจะใช้มันเพิ่ม Animation ให้กับ Next.js ได้แบบไหนบ้าง พร้อมตัวอย่างโค้ดที่เอาไปใช้ได้เลย!


🎭 Framer Motion คืออะไร?

Framer Motion เป็นไลบรารีสำหรับทำ Animation บน React และ Next.js มันใช้ง่ายมาก และทำให้เว็บดู Smooth & Interactive โดยไม่ต้องเขียน CSS Animation เอง

✅ ข้อดีของ Framer Motion

  • ใช้ Props-based Animation 👉 ไม่ต้องเขียน @keyframes
  • รองรับ Server-side Rendering (SSR) 👉 ใช้กับ Next.js ได้ดี
  • มี Gesture & Scroll Animation 👉 รองรับการลาก, สัมผัส และเลื่อน
  • ทำ Animation Complex ได้ง่ายมาก


🚀 ติดตั้ง Framer Motion ใน Next.js

เริ่มต้นง่ายๆ แค่ติดตั้งไลบรารีก่อน

npm install framer-motion

จากนั้นก็พร้อมใช้งานได้เลย! 🎉


🔥 เริ่มต้นกับ Motion Components

🎬 1️⃣ สร้าง Animation ง่ายๆ

เปิดไฟล์ pages/index.js แล้วลองใช้ Motion Component

import { motion } from "framer-motion";

export default function Home() {
  return (
    <motion.div
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 1 }}
      style={{ fontSize: "24px", textAlign: "center", marginTop: "50px" }}
    >
      Hello Framer Motion! 👋
    </motion.div>
  );
}

🔹 อธิบายโค้ด:

  • initial={{ opacity: 0 }} 👉 เริ่มต้นที่โปร่งใส (มองไม่เห็น)
  • animate={{ opacity: 1 }} 👉 ค่อยๆ เผยตัวออกมา
  • transition={{ duration: 1 }} 👉 ใช้เวลา 1 วินาที

🎉 ได้ Animation Fade-in แบบง่ายๆ แล้ว!



🏃 เพิ่ม Motion ให้ปุ่ม

🎬 2️⃣ ปุ่มที่เด้งตอนกด

import { motion } from "framer-motion";

export default function Button() {
  return (
    <motion.button
      whileTap={{ scale: 0.9 }}
      whileHover={{ scale: 1.1 }}
      style={{
        padding: "10px 20px",
        fontSize: "18px",
        borderRadius: "5px",
        border: "none",
        background: "blue",
        color: "white",
        cursor: "pointer",
      }}
    >
      Click Me 🚀
    </motion.button>
  );
}

🔹 อธิบายโค้ด:

  • whileTap={{ scale: 0.9 }} 👉 ปุ่มย่อเล็กลงนิดนึงตอนกด
  • whileHover={{ scale: 1.1 }} 👉 ปุ่มขยายขึ้นตอนเอาเมาส์ไปชี้

🛠 ปรับแต่งปุ่มให้ดูโปรได้ง่ายๆ แบบนี้เลย!


🔄 Animate หลายองค์ประกอบพร้อมกัน

🎬 3️⃣ ทำ List ที่มี Animation ตอนโหลด

import { motion } from "framer-motion";

const items = ["🍎 Apple", "🍌 Banana", "🍒 Cherry", "🍇 Grape"];

export default function List() {
  return (
    <div style={{ padding: "20px" }}>
      {items.map((item, index) => (
        <motion.div
          key={item}
          initial={{ opacity: 0, x: -50 }}
          animate={{ opacity: 1, x: 0 }}
          transition={{ duration: 0.5, delay: index * 0.2 }}
          style={{ fontSize: "20px", marginBottom: "10px" }}
        >
          {item}
        </motion.div>
      ))}
    </div>
  );
}

🔹 อธิบายโค้ด:

  • x: -50 👉 แต่ละไอเทมเริ่มจากซ้ายมือแล้วเลื่อนเข้ามา
  • delay: index * 0.2 👉 ไอเทมจะเลื่อนเข้ามาทีละตัว (ไม่มาพร้อมกัน)

📜 ได้ List ที่มี Animation นุ่มๆ แล้ว!



🎡 ใช้ Variants ให้ Animation ดูเป็นธรรมชาติ

ถ้าต้องการทำ Animation ที่มีหลายสถานะ ใช้ Variants จะช่วยให้โค้ดดูสะอาดขึ้น

🎬 4️⃣ ทำ Animation ตอนเปลี่ยนหน้าใน Next.js

import { motion } from "framer-motion";

const pageVariants = {
  hidden: { opacity: 0, y: 50 },
  visible: { opacity: 1, y: 0, transition: { duration: 0.5 } },
};

export default function Page() {
  return (
    <motion.div
      initial="hidden"
      animate="visible"
      variants={pageVariants}
      style={{ fontSize: "24px", textAlign: "center", marginTop: "50px" }}
    >
      Welcome to the new page! 🎉
    </motion.div>
  );
}

🔹 อธิบายโค้ด:

  • hidden 👉 เริ่มต้นจากด้านล่างและโปร่งใส
  • visible 👉 ขึ้นมาที่ตำแหน่งปกติ และค่อยๆ แสดงผล



📜 ทำ Scroll Animation

อยากให้ Element ขยับตอนเลื่อนหน้าจอไหม?

🎬 5️⃣ ทำให้ Element โผล่มาตอน Scroll

import { motion, useScroll } from "framer-motion";

export default function ScrollEffect() {
  const { scrollYProgress } = useScroll();

  return (
    <motion.div
      style={{
        scale: scrollYProgress,
        width: "100px",
        height: "100px",
        background: "red",
        margin: "50px auto",
      }}
    />
  );
}

🔹 อธิบายโค้ด:

  • useScroll() 👉 ใช้ตรวจจับการเลื่อน
  • scale: scrollYProgress 👉 ยิ่งเลื่อนลง Element ยิ่งขยายใหญ่

🔥 ลองเลื่อนหน้าจอดู แล้วจะเห็นความเปลี่ยนแปลง!


🏁 สรุป

  • Framer Motion ทำให้ Animation ใน Next.js ง่ายและดู Smooth
  • ใช้ motion.div เพื่อทำ Animation ได้ทันที
  • whileHover & whileTap ทำให้ปุ่ม Interactive
  • Variants ช่วยจัดการหลายสถานะของ Animation
  • useScroll ใช้ทำ Scroll Animation ได้ง่าย

🎨 ลองเล่นดู แล้วเว็บของคุณจะดูแพงขึ้น 300%!! 😆

วันพุธที่ 26 มีนาคม พ.ศ. 2568

การใช้ Component Library ใน Next.js: เลือก Chakra UI, MUI หรือ shadcn/ui ดี?


ถ้าไม่อยากเขียน CSS เอง ให้ Component Library จัดการให้เถอะ!

การทำเว็บให้ดูดี ไม่ใช่แค่โค้ดดี แต่ต้องมี UI ที่สวยด้วย แต่การเขียน CSS เองมันก็เหนื่อยใช่ไหม? 😵‍💫 ถ้างั้นลองใช้ Component Library ที่มี UI สวยๆ ให้ใช้เลยดีกว่า

ในบทความนี้ เราจะมาแนะนำ 3 ตัวเลือกยอดนิยมสำหรับ Next.js ได้แก่ Chakra UI, MUI และ shadcn/ui พร้อมตัวอย่างโค้ดให้คุณลองใช้เลย

🚀 Component Library คืออะไร?

Component Library คือชุดของ UI Components (เช่น ปุ่ม, ฟอร์ม, การ์ด) ที่ออกแบบมาพร้อมใช้งาน ช่วยให้เรา ไม่ต้องเขียน CSS เอง และยังได้ UX ที่ดีขึ้น

✅ ทำไมต้องใช้ Component Library?

  • ลดเวลาทำงาน 👉 ไม่ต้องสร้าง UI จากศูนย์
  • รองรับ Responsive 👉 ใช้ได้ทั้งมือถือและเดสก์ท็อป
  • เข้ากันได้ดีกับ Next.js 👉 โหลดเร็ว, รองรับ SSR


Chakra UI – UI ที่ใช้ง่ายสุดๆ 😍

🔹 ข้อดีของ Chakra UI

✅ ใช้ Props-based Styling (ไม่ต้องเขียน CSS)
✅ รองรับ Dark Mode อัตโนมัติ
✅ มี Flexbox & Grid พร้อมใช้

🛠 ติดตั้ง Chakra UI

npm install @chakra-ui/react @emotion/react @emotion/styled framer-motion

🌟 ใช้ Chakra UI ใน Next.js

1️⃣ ห่อแอปด้วย <ChakraProvider>

แก้ไข pages/_app.js

import { ChakraProvider } from "@chakra-ui/react";

export default function MyApp({ Component, pageProps }) {
  return (
    <ChakraProvider>
      <Component {...pageProps} />
    </ChakraProvider>
  );
}


2️⃣ ลองใช้ปุ่มจาก Chakra UI

สร้าง pages/index.js

import { Button, Box, Text } from "@chakra-ui/react";

export default function Home() {
  return (
    <Box p={5}>
      <Text fontSize="xl">Hello Chakra UI! 👋</Text>
      <Button colorScheme="blue" mt={3}>
        Click Me
      </Button>
    </Box>
  );
}

🔥 จุดเด่น:

  • colorScheme="blue" 👉 เปลี่ยนสีปุ่มง่ายๆ
  • Box 👉 ใช้แทน <div> และมี spacing props เช่น p={5}



MUI (Material UI) – UI มาตรฐานองค์กร 🏢

🔹 ข้อดีของ MUI

✅ ดีไซน์มาตรฐาน Google Material Design
✅ คอมโพเนนต์เยอะมาก (Button, Table, Modal ฯลฯ)
✅ มี Theme System ปรับแต่งได้

🛠 ติดตั้ง MUI

npm install @mui/material @emotion/react @emotion/styled

🌟 ใช้ MUI ใน Next.js

1️⃣ ห่อแอปด้วย <ThemeProvider>

แก้ไข pages/_app.js

import { ThemeProvider, createTheme } from "@mui/material/styles";

const theme = createTheme();

export default function MyApp({ Component, pageProps }) {
  return (
    <ThemeProvider theme={theme}>
      <Component {...pageProps} />
    </ThemeProvider>
  );
}

2️⃣ ลองใช้ปุ่มจาก MUI

สร้าง pages/index.js

import { Button, Container, Typography } from "@mui/material";

export default function Home() {
  return (
    <Container>
      <Typography variant="h4" gutterBottom>
        Welcome to MUI 🚀
      </Typography>
      <Button variant="contained" color="primary">
        Click Me
      </Button>
    </Container>
  );
}

🔥 จุดเด่น:

  • variant="contained" 👉 ปุ่มแบบพื้นหลังทึบ
  • Typography 👉 ใช้แทน <h1> หรือ <p>


shadcn/ui – UI ที่เบาและยืดหยุ่นสุดๆ 😎

🔹 ข้อดีของ shadcn/ui

✅ ใช้ Tailwind CSS (แต่งสไตล์ง่าย)
✅ ไม่มี JS Runtime (เร็วมาก)
✅ คัดลอกโค้ดมาใช้ได้เลย

🛠 ติดตั้ง shadcn/ui

npx shadcn-ui@latest init

จากนั้นเลือก "Install components" เพื่อนำคอมโพเนนต์มาใช้

🌟 ใช้ shadcn/ui ใน Next.js

1️⃣ ติดตั้ง Button

npx shadcn-ui@latest add button

2️⃣ ลองใช้ปุ่มจาก shadcn/ui

สร้าง pages/index.js

import { Button } from "@/components/ui/button";

export default function Home() {
  return (
    <div className="p-5">
      <h1 className="text-xl font-bold">Hello shadcn/ui!</h1>
      <Button className="mt-3">Click Me</Button>
    </div>
  );
}

🔥 จุดเด่น:

  • ใช้ Tailwind ได้เลย (className="text-xl")
  • ปรับแต่งสไตล์เองได้เต็มที่


🤔 สรุป: ควรเลือกตัวไหนดี?

Library เหมาะกับใคร?
Chakra UI ถ้าต้องการ Props-based Styling และใช้ง่าย
MUI ถ้าต้องการ Material Design และคอมโพเนนต์เยอะ
shadcn/ui ถ้าต้องการ UI เร็ว + ใช้ Tailwind

👉 ถ้าคุณชอบ:

  • ความง่าย + Dark Mode อัตโนมัติ 👉 Chakra UI
  • คอมโพเนนต์เยอะ + สไตล์องค์กร 👉 MUI
  • ใช้ Tailwind + เร็วที่สุด 👉 shadcn/ui


🎉 สรุป

  • Component Library ช่วยให้ทำเว็บเร็วขึ้น
  • Chakra UI ใช้ง่ายสุด ปรับแต่งสะดวก
  • MUI เหมาะกับแอปองค์กร
  • shadcn/ui เบา เร็ว ใช้ Tailwind ได้เต็มที่

ลองใช้ดู แล้วจะติดใจ! 😉

วันอังคารที่ 25 มีนาคม พ.ศ. 2568

วิธีป้องกันหน้าเว็บที่ต้องล็อกอินก่อนเข้าใช้งานใน Next.js

อย่าปล่อยให้ใครเข้ามาเที่ยวเว็บเราฟรีๆ! 😆

ถ้าคุณทำเว็บที่มีหน้า Dashboard, Profile หรือหน้าข้อมูลสำคัญ คุณคงไม่อยากให้ใครก็ได้เข้าถึงข้อมูลพวกนั้นได้ง่ายๆ เราต้องล็อกอินก่อนถึงจะเข้าได้!

ในบทความนี้ เราจะมาดูกันว่า Next.js + NextAuth.js ใช้ทำ Protected Route (หน้าที่ต้องล็อกอินก่อนเข้า) ได้ยังไง

🚀 ทำไมต้องป้องกันหน้าเว็บ?

✅ ป้องกันข้อมูลสำคัญ

  • ถ้าเว็บมีข้อมูลผู้ใช้ ห้ามให้คนที่ไม่ได้ล็อกอินเข้าถึง

✅ เพิ่มความปลอดภัย

  • ไม่เปิดช่องให้ Bot หรือ Hacker แอบเข้ามาดูข้อมูล

✅ UX ดีขึ้น

  •     ถ้าไม่ได้ล็อกอิน 👉 Redirect ไปหน้า Login
  •     ล็อกอินแล้ว 👉 เข้าใช้งานได้ปกติ

สรุป: ทำให้เว็บ ปลอดภัย + เป็นระเบียบ + ใช้งานง่าย


🔥 วิธีทำ Protected Route ใน Next.js

1️⃣ ติดตั้ง NextAuth.js

ถ้ายังไม่มี NextAuth.js ให้ติดตั้งก่อน

npm install next-auth

2️⃣ ตั้งค่า NextAuth.js

สร้างไฟล์ pages/api/auth/[...nextauth].js

import NextAuth from "next-auth";
import GoogleProvider from "next-auth/providers/google";

export default NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
});

💡 Tip: เราใช้ Google Login แต่คุณเปลี่ยนไปใช้ Auth0, GitHub, หรือ Email/Password ได้

3️⃣ ตั้งค่า ENV

สร้างไฟล์ .env.local แล้วใส่ค่า

GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
NEXTAUTH_SECRET=your-random-secret
NEXTAUTH_URL=http://localhost:3000


🛑 ป้องกันหน้าที่ต้องล็อกอิน

❌ วิธีที่ 1: เช็กใน Component

import { useSession, signIn } from "next-auth/react";

export default function Dashboard() {
  const { data: session, status } = useSession();

  if (status === "loading") return <p>Loading...</p>;
  if (!session) return <button onClick={() => signIn()}>Login to access</button>;

  return <h1>Welcome to your Dashboard, {session.user.name}!</h1>;
}

🔹 อธิบายโค้ด:
  •     ถ้ากำลังโหลด ให้แสดง Loading
  •     ถ้าไม่ได้ล็อกอิน ให้แสดงปุ่ม Login
  •     ถ้าล็อกอินแล้ว แสดงข้อมูลได้ปกติ

🚀 วิธีที่ 2: Redirect ไปหน้า Login อัตโนมัติ

ใช้ getServerSideProps เช็กสิทธิ์ก่อนโหลดหน้า

import { getSession } from "next-auth/react";

export async function getServerSideProps(context) {
  const session = await getSession(context);

  if (!session) {
    return {
      redirect: {
        destination: "/login",
        permanent: false,
      },
    };
  }

  return {
    props: { session },
  };
}

export default function Dashboard({ session }) {
  return <h1>Welcome, {session.user.name}!</h1>;
}

🔹 อธิบายโค้ด:
  •     ถ้า ไม่ได้ล็อกอิน 👉 Redirect ไป /login
  •     ถ้า ล็อกอินแล้ว 👉 ให้เข้า Dashboard ได้

✅ ข้อดี:
  •     ป้องกันตั้งแต่ฝั่งเซิร์ฟเวอร์
  •     SEO Friendly เพราะไม่ได้บล็อกการโหลดหน้า

🔒 วิธีที่ 3: สร้าง Middleware ป้องกันทุกหน้า

ใช้ middleware.ts เพื่อบังคับให้ล็อกอินทุกครั้งที่เข้า /dashboard

สร้างไฟล์ middleware.ts

import { NextRequest, NextResponse } from "next/server";

export function middleware(req: NextRequest) {
  const token = req.cookies.get("next-auth.session-token");

  if (!token) {
    return NextResponse.redirect(new URL("/login", req.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: "/dashboard/:path*",
};

🔹 อธิบายโค้ด:
  •     ถ้าไม่มี Token 👉 Redirect ไป /login
  •     ถ้ามี Token 👉 อนุญาตให้เข้า /dashboard ได้

✅ ข้อดี:
  •     ป้องกันทุกหน้าใน /dashboard/ อัตโนมัติ
  •     ไม่ต้องใส่โค้ดป้องกันทุกไฟล์

🎨 สร้างหน้า Login

สร้าง pages/login.js

import { signIn } from "next-auth/react";

export default function Login() {
  return (
    <div>
      <h1>Login</h1>
      <button onClick={() => signIn("google")}>Login with Google</button>
    </div>
  );
}

🔹 อธิบายโค้ด:
  •     ปุ่มเดียวจบ! กดแล้วล็อกอินผ่าน Google


❓ คำถามที่พบบ่อย

1️⃣ ควรใช้วิธีไหนดีที่สุด?

ขึ้นอยู่กับโปรเจ็กต์:
  •     หน้าเดียวกัน (Client-side protection) 👉 useSession()
  •     ป้องกันทุกหน้า (Server-side protection) 👉 getServerSideProps()
  •     ป้องกันโฟลเดอร์ (Global protection) 👉 middleware.ts

2️⃣ NextAuth.js รองรับอะไรบ้าง?

  •     Google, Facebook, GitHub, Apple, Twitter
  •     Email/Password
  •     Magic Link

3️⃣ ถ้าอยากให้บางหน้าล็อกอิน แต่บางหน้าไม่ต้องทำไง?

  •     ใช้ Middleware เฉพาะโฟลเดอร์
  •     เช็ก useSession() ใน Component

🎉 สรุป

  •     ป้องกันหน้าเว็บที่ต้องล็อกอินก่อนเข้าใช้งาน ได้หลายวิธี
  •     ใช้ useSession() ถ้าต้องการป้องกันจากฝั่ง Client
  •     ใช้ getServerSideProps() ถ้าต้องการ Redirect อัตโนมัติ
  •     ใช้ middleware.ts ถ้าต้องการป้องกันทั้งโฟลเดอร์
ลองทำดู แล้วจะติดใจ! 😉

วันจันทร์ที่ 24 มีนาคม พ.ศ. 2568

วิธีใช้ Auth0 กับ Next.js แบบเข้าใจง่าย

ถ้าขี้เกียจทำระบบล็อกอินเอง ให้ Auth0 จัดการให้เถอะ!

Auth0 เป็นแพลตฟอร์มจัดการการยืนยันตัวตน (Authentication) ที่ช่วยให้เราทำระบบล็อกอินได้เร็วขึ้น โดยไม่ต้องมานั่งเขียนโค้ดยาวๆ ทำให้เว็บปลอดภัยขึ้นแบบไม่ต้องปวดหัว

ในบทความนี้ เราจะมาใช้ Auth0 กับ Next.js เพื่อทำระบบล็อกอินที่รองรับ OAuth (Google, Facebook ฯลฯ) และ Email/Password ได้ง่ายๆ


🚀 ทำไมต้องใช้ Auth0?

✅ ลดเวลาพัฒนา

  •     ไม่ต้องสร้างระบบยืนยันตัวตนเอง
  •     มี UI สำเร็จรูปให้ใช้เลย

✅ รองรับหลายวิธี

  •     ล็อกอินด้วย Google, Facebook, Apple, GitHub ฯลฯ
  •     ใช้ Magic Link หรือ OTP ก็ได้
  •     ใช้ Username/Password แบบปกติก็ได้

✅ ปลอดภัยกว่า

  •     มีการเข้ารหัสและป้องกันแฮก
  •     รองรับ MFA (Multi-Factor Authentication)


สรุปง่ายๆ Auth0 = เร็ว + ปลอดภัย + ใช้งานง่าย


🎯 มาเริ่มกันเลย!

1️⃣ สร้างโปรเจ็กต์ Next.js

ถ้ายังไม่มีโปรเจ็กต์ Next.js ให้สร้างก่อน

npx create-next-app@latest auth0-demo
cd auth0-demo
npm install

2️⃣ สมัครและตั้งค่า Auth0

  1.     ไปที่ Auth0 Dashboard แล้วสมัครบัญชี
  2.     สร้าง Application ใหม่
    • เลือก Single Page Application
    • ใส่ Callback URL: http://localhost:3000/api/auth/callback/auth0
    • ใส่ Logout URL: http://localhost:3000/
  3. คัดลอก Client ID และ Client Secret


3️⃣ ติดตั้ง NextAuth.js (ใช้ Auth0 เป็น Provider)

ติดตั้งแพ็กเกจที่ต้องใช้

npm install next-auth @auth0/nextjs-auth0

4️⃣ ตั้งค่า NextAuth.js

สร้างไฟล์ pages/api/auth/[...nextauth].js

import NextAuth from "next-auth";
import Auth0Provider from "next-auth/providers/auth0";

export default NextAuth({
  providers: [
    Auth0Provider({
      clientId: process.env.AUTH0_CLIENT_ID,
      clientSecret: process.env.AUTH0_CLIENT_SECRET,
      issuer: process.env.AUTH0_ISSUER,
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
});

5️⃣ ตั้งค่า ENV

สร้างไฟล์ .env.local แล้วใส่ค่าเหล่านี้

AUTH0_CLIENT_ID=your-client-id
AUTH0_CLIENT_SECRET=your-client-secret
AUTH0_ISSUER=https://your-auth0-domain/
NEXTAUTH_SECRET=your-random-secret
NEXTAUTH_URL=http://localhost:3000

💡 Tip: ค่า NEXTAUTH_SECRET ใช้คำสั่งนี้สร้างแบบสุ่มได้

openssl rand -base64 32

6️⃣ สร้างปุ่ม Login / Logout

สร้างไฟล์ pages/index.js

import { useSession, signIn, signOut } from "next-auth/react";

export default function Home() {
  const { data: session } = useSession();

  return (
    <div>
      <h1>Next.js + Auth0</h1>
      {session ? (
        <>
          <p>Welcome, {session.user.name}!</p>
          <button onClick={() => signOut()}>Logout</button>
        </>
      ) : (
        <button onClick={() => signIn("auth0")}>Login with Auth0</button>
      )}
    </div>
  );
}

🔹 อธิบายโค้ด:

  •     ใช้ useSession() เช็กว่าผู้ใช้ล็อกอินหรือยัง
  •     ถ้าล็อกอินแล้ว แสดงชื่อ + ปุ่ม Logout
  •     ถ้ายังไม่ล็อกอิน แสดงปุ่ม Login


🔥 ทดสอบใช้งาน

  1.     รันเซิร์ฟเวอร์ npm run dev
  2.     เข้าไปที่หน้าเว็บ http://localhost:3000
  3.     กดปุ่ม "Login with Auth0"
  4.     ล็อกอินผ่าน Google หรือ Email/Password
  5.     ถ้าสำเร็จจะกลับมาหน้าเว็บและแสดงชื่อ! 🎉


🎨 ทำหน้า Profile

สร้างไฟล์ pages/profile.js

import { useUser } from "@auth0/nextjs-auth0/client";

export default function Profile() {
  const { user, error, isLoading } = useUser();

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    user && (
      <div>
        <h1>{user.name}</h1>
        <p>Email: {user.email}</p>
        <img src={user.picture} alt={user.name} width={100} />
      </div>
    )
  );
}

🔹 อธิบายโค้ด:

  •     ใช้ useUser() ดึงข้อมูลผู้ใช้จาก Auth0
  •     แสดง ชื่อ, อีเมล, รูปโปรไฟล์


❓ คำถามที่พบบ่อย

1️⃣ Auth0 ใช้ฟรีไหม?

ใช่! แต่แผนฟรีจำกัด 7,000 ล็อกอินต่อเดือน ถ้าใช้เยอะกว่านี้ต้องอัปเกรด

2️⃣ รองรับ Social Login อะไรบ้าง?

Google, Facebook, Apple, GitHub, Twitter ฯลฯ

3️⃣ ถ้าอยากใช้ Magic Link ต้องทำไง?

Auth0 รองรับ Passwordless Login (Magic Link, OTP) แค่เปิดใช้งานใน Dashboard


🎉 สรุป

  •     Auth0 ช่วยให้ทำระบบล็อกอินได้เร็วขึ้น
  •     ใช้ NextAuth.js เชื่อม Auth0 กับ Next.js
  •     รองรับ Social Login, Email/Password, Magic Link
  •     ปลอดภัย ไม่ต้องจัดการรหัสผ่านเอง

ลองทำดู แล้วจะติดใจ! 😉

การใช้ Image Optimization ใน Next.js: โหลดไว SEO ดี ประหยัดแบนด์วิธ!

เคยเจอปัญหาเว็บโหลดช้าเพราะรูปภาพขนาดใหญ่หรือเปล่า? 📷 ถ้าใช่ Next.js มีฟีเจอร์ Image Optimization ที่ช่วยให้รูปภาพโหลดเร็วขึ้น อัตโนมัติ ไม...