วันจันทร์ที่ 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
  •     ปลอดภัย ไม่ต้องจัดการรหัสผ่านเอง

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

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

วิธีใช้ Magic Link Authentication ใน Next.js แบบเข้าใจง่าย

ถ้าเบื่อรหัสผ่าน มาลอง Magic Link กันเถอะ!

Magic Link Authentication เป็นวิธีล็อกอินที่ไม่ต้องใช้รหัสผ่าน ผู้ใช้แค่ใส่อีเมล แล้วระบบจะส่งลิงก์ให้คลิกเพื่อเข้าสู่ระบบ ง่าย สะดวก และปลอดภัยกว่าการจำรหัสผ่านเยอะ

ในบทความนี้ เราจะมาดูกันว่า Next.js ใช้ Magic Link ได้ยังไง โดยใช้ NextAuth.js (ตัวช่วยจัดการการยืนยันตัวตนใน Next.js) พร้อมกับ Resend (บริการส่งอีเมลที่ใช้ง่ายและเร็ว)

🎩 ทำไมต้อง Magic Link?

✅ ไม่ต้องจำรหัสผ่าน

  • ชีวิตเรามีรหัสผ่านเยอะเกินไปแล้ว! Magic Link ตัดปัญหานี้ออกไป

✅ ปลอดภัยกว่ารหัสผ่านปกติ

  •     ไม่มีรหัสผ่านให้แฮก (เพราะไม่มีเลย 🤣)
  •     ใช้ one-time link (ลิงก์ใช้ได้ครั้งเดียว) ลดความเสี่ยง

✅ UX ดีขึ้น

  •     ล็อกอินง่าย ไม่ต้องพิมพ์อะไรเยอะ
  •     ลดปัญหา "ลืมรหัสผ่าน"


สรุป: Magic Link = ง่าย + ปลอดภัย + ดีต่อผู้ใช้


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

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

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

npx create-next-app@latest magic-link-demo
cd magic-link-demo
npm install

2️⃣ ติดตั้ง NextAuth.js และ Resend

npm install next-auth resend

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

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

import NextAuth from "next-auth";
import EmailProvider from "next-auth/providers/email";
import { Resend } from "resend";

const resend = new Resend(process.env.RESEND_API_KEY);

export default NextAuth({
  providers: [
    EmailProvider({
      async sendVerificationRequest({ identifier: email, url }) {
        await resend.emails.send({
          from: "no-reply@yourdomain.com",
          to: email,
          subject: "Magic Link Login",
          html: `<p>Click <a href="${url}">here</a> to log in.</p>`,
        });
      },
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
});

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

  •     ใช้ EmailProvider จาก NextAuth.js
  •     เมื่อผู้ใช้ใส่อีเมล ระบบจะส่ง Magic Link ไปที่อีเมลนั้น
  •     ใช้ Resend เป็นตัวช่วยส่งอีเมล
  •     ลิงก์ที่ส่งไปเป็นแบบ one-time link


🎨 สร้างหน้า Login

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

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

export default function Login() {
  const handleLogin = async (e) => {
    e.preventDefault();
    const email = e.target.email.value;
    await signIn("email", { email });
  };

  return (
    <div>
      <h1>Magic Link Login</h1>
      <form onSubmit={handleLogin}>
        <input type="email" name="email" placeholder="Enter your email" required />
        <button type="submit">Send Magic Link</button>
      </form>
    </div>
  );
}

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

  •     มีฟอร์มให้ผู้ใช้กรอกอีเมล
  •     กดปุ่มแล้วระบบจะเรียก signIn("email", { email }) เพื่อขอ Magic Link
  •     NextAuth.js จะจัดการส่งอีเมลให้เอง


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

  1.     รันเซิร์ฟเวอร์ npm run dev
  2.     เข้าไปที่ /login
  3.     ใส่อีเมล แล้วกดส่ง
  4.     เปิดอีเมล กดลิงก์ แล้วเข้าสู่ระบบ! 🎉



🔥 เพิ่มเติม: ตั้งค่า ENV

สร้างไฟล์ .env.local

NEXTAUTH_SECRET=your-secret-key
RESEND_API_KEY=your-resend-api-key
NEXTAUTH_URL=http://localhost:3000


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

1️⃣ ถ้าไม่มีอีเมล จะล็อกอินยังไง?

Magic Link ต้องใช้อีเมลเท่านั้น ถ้าผู้ใช้ไม่มีอีเมล อาจต้องใช้ OAuth (เช่น Google, Facebook)

2️⃣ Magic Link ปลอดภัยจริงไหม?

ค่อนข้างปลอดภัย แต่ควร:

  •     ใช้ one-time link
  •     ลิงก์ควรหมดอายุเร็ว (เช่น 10 นาที)
  •     ใช้ HTTPS เสมอ

3️⃣ ถ้าอีเมลไปไม่ถึงทำไง?

  •     เช็กโฟลเดอร์ Spam
  •     ใช้อีเมลจากผู้ให้บริการที่น่าเชื่อถือ
  •     ลองเปลี่ยนไปใช้ SMTP แทน Resend


🎉 สรุป

  •     Magic Link คือการล็อกอินแบบไม่ใช้รหัสผ่าน
  •     NextAuth.js + Resend ช่วยให้ใช้งาน Magic Link ได้ง่าย
  •     UX ดีขึ้น ปลอดภัยขึ้น และลดปัญหาลืมรหัสผ่าน

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

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

การทำ Role-based Authentication ใน Next.js – แยกสิทธิ์ Admin, User, และ Guest

อยากให้เว็บ Next.js ของเรามีระบบแบ่งสิทธิ์การเข้าถึง (RBAC – Role-Based Access Control) ไหม?
เช่น Admin ดูข้อมูลทั้งหมดได้, User ใช้งานปกติ, Guest เข้าได้เฉพาะบางหน้า

🎯 วันนี้เราจะมาทำ Role-based Authentication ใน Next.js

✅ รองรับ JWT Authentication
✅ ใช้ Middleware เช็ก Role ก่อนเข้าหน้าเว็บ
✅ มีตัวอย่าง API แยกสิทธิ์ Admin และ User

🎯 Role-based Authentication คืออะไร?

RBAC (Role-Based Access Control) คือ การกำหนดสิทธิ์ของผู้ใช้ตามบทบาท (Role) เช่น

  • Admin 🛡️ – แก้ไขข้อมูลทุกอย่างได้
  • User 👤 – ใช้งานฟีเจอร์ปกติ
  • Guest 👥 – ดูข้อมูลบางอย่างได้

💡 เราจะใช้ JWT (JSON Web Token) ในการจัดการ Authentication & Role


🚀 1. ติดตั้งโปรเจกต์ Next.js และไลบรารี

เริ่มต้นด้วยการสร้างโปรเจกต์ Next.js และติดตั้งไลบรารีที่จำเป็น

npx create-next-app role-based-auth
cd role-based-auth
npm install jsonwebtoken bcryptjs cookie

  • jsonwebtoken – ใช้สำหรับสร้างและตรวจสอบ JWT
  • bcryptjs – ใช้เข้ารหัสรหัสผ่าน
  • cookie – ใช้จัดการ Token ผ่าน Cookie


🔐 2. สร้างระบบ Authentication (JWT)

📌 สร้าง API Register (สมัครสมาชิก)

📌 ไฟล์: pages/api/auth/register.js

import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

const users = []; // จำลองฐานข้อมูลใน Memory

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { email, password, role } = req.body;
  if (!email || !password || !role) return res.status(400).json({ error: "Missing fields" });

  const hashedPassword = await bcrypt.hash(password, 10);
  const newUser = { email, password: hashedPassword, role };
  users.push(newUser);

  res.json({ message: "User registered", user: { email, role } });
}

✅ สมัครสมาชิก พร้อมเก็บ role (เช่น admin หรือ user)

📌 สร้าง API Login (เข้าสู่ระบบ)

📌 ไฟล์: pages/api/auth/login.js

import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";
import { serialize } from "cookie";

const users = []; // ใช้ฐานข้อมูลจริงในโปรเจกต์จริง

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { email, password } = req.body;
  const user = users.find((u) => u.email === email);
  if (!user) return res.status(401).json({ error: "User not found" });

  const isMatch = await bcrypt.compare(password, user.password);
  if (!isMatch) return res.status(401).json({ error: "Invalid password" });

  const token = jwt.sign({ email: user.email, role: user.role }, "SECRET_KEY", { expiresIn: "1h" });

  res.setHeader("Set-Cookie", serialize("auth_token", token, { httpOnly: true, path: "/" }));
  res.json({ message: "Login successful", token });
}

✅ ตรวจสอบรหัสผ่าน และสร้าง JWT Token
✅ เก็บ Token ใน Cookie เพื่อใช้ใน Middleware

🚦 3. สร้าง Middleware เช็ก Role

📌 ไฟล์: middleware/auth.js

import jwt from "jsonwebtoken";
import { parse } from "cookie";

export function withAuth(handler, allowedRoles) {
  return async (req, res) => {
    const cookies = parse(req.headers.cookie || "");
    const token = cookies.auth_token;

    if (!token) return res.status(401).json({ error: "Not authenticated" });

    try {
      const decoded = jwt.verify(token, "SECRET_KEY");
      if (!allowedRoles.includes(decoded.role)) {
        return res.status(403).json({ error: "Access Denied" });
      }

      req.user = decoded; // ส่งข้อมูลผู้ใช้ไปยัง API
      return handler(req, res);
    } catch (error) {
      return res.status(401).json({ error: "Invalid Token" });
    }
  };
}

✅ เช็กว่า JWT ถูกต้อง & Role ตรงกับสิทธิ์ที่กำหนด

🔥 4. สร้าง API แยกสิทธิ์

📌 API เฉพาะ Admin

📌 ไฟล์: pages/api/admin.js

import { withAuth } from "../../middleware/auth";

export default withAuth((req, res) => {
  res.json({ message: "Welcome Admin!", user: req.user });
}, ["admin"]);

✅ เฉพาะ Admin เท่านั้นที่เรียก API นี้ได้

📌 API สำหรับ User & Admin

📌 ไฟล์: pages/api/user.js

import { withAuth } from "../../middleware/auth";

export default withAuth((req, res) => {
  res.json({ message: `Welcome ${req.user.role}!`, user: req.user });
}, ["user", "admin"]);

✅ ทั้ง User & Admin ใช้งานได้


🎨 5. สร้าง UI หน้าเว็บ

📌 ไฟล์: pages/index.js

import { useState } from "react";

export default function Home() {
  const [message, setMessage] = useState("");

  const checkAdmin = async () => {
    const res = await fetch("/api/admin");
    const data = await res.json();
    setMessage(data.message || data.error);
  };

  const checkUser = async () => {
    const res = await fetch("/api/user");
    const data = await res.json();
    setMessage(data.message || data.error);
  };

  return (
    <div>
      <h1>Role-based Authentication</h1>
      <button onClick={checkAdmin}>Check Admin</button>
      <button onClick={checkUser}>Check User</button>
      <p>{message}</p>
    </div>
  );
}

✅ ทดสอบ กดปุ่ม Check Admin หรือ Check User เพื่อดูสิทธิ์



🚀 6. ทดสอบระบบ

  1. รันเซิร์ฟเวอร์ npm run dev
  2. สมัครสมาชิก → /api/auth/register
  3. ล็อกอิน → /api/auth/login
  4. ลองเรียก /api/admin และ /api/user ดูว่า Role ตรงไหม


🎯 สรุป

✅ ใช้ JWT และ Middleware จัดการ Role
✅ แยกสิทธิ์ Admin, User, Guest
✅ ใช้ API แยก Role เพื่อควบคุมสิทธิ์การเข้าถึง

💡 พร้อมใช้กับเว็บจริง! แค่เพิ่ม Database และ UI สวยๆ เท่านั้น! 🚀

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

การใช้ Firebase Authentication กับ Next.js – ล็อกอินง่ายๆ ด้วย Google, Email, GitHub

อยากให้เว็บ Next.js ของเรามีระบบล็อกอินด้วย Google, Email หรือ GitHub ไหม? 🔑
Firebase Authentication เป็นตัวช่วยที่ ฟรี, ใช้งานง่าย, และ ปลอดภัย

วันนี้เราจะมาทำระบบ Sign-up & Login ด้วย Firebase Auth ใน Next.js แบบครบๆ พร้อมโค้ดตัวอย่าง! 🚀

🎯 เราจะทำอะไรบ้าง?

✅ สมัครสมาชิก & ล็อกอินด้วย Email/Password
✅ ล็อกอินด้วย Google, GitHub
✅ จัดการ Session ด้วย Firebase
✅ ใช้ Firebase SDK ใน Next.js

💡 ไม่ต้องทำระบบ Auth เอง! Firebase จัดการให้หมด แค่เขียนโค้ดเชื่อมต่อก็ใช้ได้เลย 🔥

🔥 ตั้งค่า Firebase Authentication

🛠️ 1. สมัคร Firebase และสร้างโปรเจกต์

  1. เข้าไปที่ Firebase Console
  2. กด "Add Project" → ตั้งชื่อโปรเจกต์ → กด "Continue"
  3. กด "Enable Google Analytics" หรือ ปิดก็ได้ → กด "Create Project"

🔑 2. เปิดใช้งาน Authentication

  1. ไปที่ "Build" → "Authentication"
  2. กด "Get Started"
  3. ไปที่ "Sign-in method"
  4. เปิดการใช้งาน Email/Password, Google, GitHub

🔗 3. ตั้งค่า Web API Key

  1. ไปที่ "Project settings" (ไอคอนรูปเฟือง)
  2. ไปที่ "General" → "Your apps" → กด "Web app"
  3. ตั้งชื่อแอป → กด "Register app"
  4. คัดลอก Firebase Config ไว้


⚙️ ติดตั้ง Firebase SDK ใน Next.js

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

    npx create-next-app my-firebase-auth
    cd my-firebase-auth
    npm install firebase

2. สร้างไฟล์ firebase.js เพื่อเชื่อมต่อ Firebase

📌 โค้ด: lib/firebase.js

import { initializeApp } from "firebase/app";
import { getAuth } from "firebase/auth";

const firebaseConfig = {
  apiKey: "YOUR_API_KEY",
  authDomain: "YOUR_AUTH_DOMAIN",
  projectId: "YOUR_PROJECT_ID",
  storageBucket: "YOUR_STORAGE_BUCKET",
  messagingSenderId: "YOUR_MESSAGING_SENDER_ID",
  appId: "YOUR_APP_ID",
};

const app = initializeApp(firebaseConfig);
export const auth = getAuth(app);

💡 อย่าลืมเปลี่ยน YOUR_API_KEY เป็นค่าจริงจาก Firebase!



✉️ สมัครสมาชิก & ล็อกอินด้วย Email/Password

📌 โค้ด: pages/auth.js

import { useState } from "react";
import { auth } from "../lib/firebase";
import { createUserWithEmailAndPassword, signInWithEmailAndPassword } from "firebase/auth";

export default function AuthPage() {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [message, setMessage] = useState("");

  const signUp = async () => {
    try {
      await createUserWithEmailAndPassword(auth, email, password);
      setMessage("Signup successful!");
    } catch (error) {
      setMessage(error.message);
    }
  };

  const login = async () => {
    try {
      await signInWithEmailAndPassword(auth, email, password);
      setMessage("Login successful!");
    } catch (error) {
      setMessage(error.message);
    }
  };

  return (
    <div>
      <h1>Firebase Auth</h1>
      <input type="email" placeholder="Email" onChange={(e) => setEmail(e.target.value)} />
      <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
      <button onClick={signUp}>Sign Up</button>
      <button onClick={login}>Login</button>
      <p>{message}</p>
    </div>
  );
}

✅ กรอก Email & Password แล้วสมัครหรือล็อกอินได้เลย!


🔵 ล็อกอินด้วย Google

📌 โค้ด: pages/auth.js (เพิ่มปุ่ม Google Sign-in)

import { GoogleAuthProvider, signInWithPopup } from "firebase/auth";

const loginWithGoogle = async () => {
  try {
    const provider = new GoogleAuthProvider();
    await signInWithPopup(auth, provider);
    setMessage("Login with Google successful!");
  } catch (error) {
    setMessage(error.message);
  }
};

// เพิ่มปุ่ม Login with Google
<button onClick={loginWithGoogle}>Login with Google</button>;

✅ กดปุ่ม → เลือกบัญชี Google → ล็อกอินสำเร็จ!


🛡️ จัดการ Session ด้วย Firebase

Firebase มี onAuthStateChanged ให้เราตรวจสอบว่าผู้ใช้ล็อกอินอยู่หรือไม่

📌 โค้ด: pages/index.js


import { useEffect, useState } from "react";
import { auth } from "../lib/firebase";
import { onAuthStateChanged, signOut } from "firebase/auth";

export default function Home() {
  const [user, setUser] = useState(null);

  useEffect(() => {
    const unsubscribe = onAuthStateChanged(auth, (user) => {
      setUser(user);
    });
    return () => unsubscribe();
  }, []);

  return (
    <div>
      <h1>Welcome {user ? user.email : "Guest"}</h1>
      {user ? (
        <button onClick={() => signOut(auth)}>Logout</button>
      ) : (
        <a href="/auth">Go to Login</a>
      )}
    </div>
  );
}

✅ ถ้าล็อกอินอยู่จะแสดง Email ของผู้ใช้
✅ ถ้ายังไม่ล็อกอินจะแสดงปุ่ม Go to Login


🚀 ทดสอบระบบ

  1. รันเซิร์ฟเวอร์ npm run dev
  2. เปิด http://localhost:3000/auth
  3. ลองสมัครสมาชิก & ล็อกอิน
  4. เช็คหน้าแรก ว่ามี Session หรือไม่?

🎉 เรียบร้อย! Firebase Auth ใช้งานได้แล้ว!


🎯 สรุป

✅ ใช้ Firebase Authentication ใน Next.js ได้ง่ายๆ
✅ รองรับ Email/Password, Google, GitHub
✅ Session จัดการอัตโนมัติ ด้วย Firebase SDK
✅ ใช้ onAuthStateChanged เช็กสถานะผู้ใช้

🚀 พร้อมใช้ในโปรเจกต์จริง! แค่เพิ่ม UI สวยๆ และ Database ก็เปิดให้คนใช้ได้เลย!

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

การจัดการ Session และ Cookie ใน Next.js แบบเข้าใจง่าย!

Session และ Cookie เป็นสิ่งที่ขาดไม่ได้ในระบบเว็บแอป Next.js ไม่ว่าจะเป็นการ จัดการผู้ใช้ (Authentication), จดจำค่าต่างๆ หรือ รักษาความปลอดภัยของแอป

📌 บทความนี้จะอธิบายว่า Session & Cookie คืออะไร และ ใช้งานใน Next.js ยังไง พร้อมตัวอย่างโค้ดแบบจับมือทำ! 🚀

🎯 Session และ Cookie คืออะไร?

🍪 Cookie

Cookie เป็นข้อมูลเล็กๆ ที่ browser เก็บไว้ในเครื่องของผู้ใช้ โดยส่งไปพร้อมกับทุกๆ request เพื่อให้เซิร์ฟเวอร์จำผู้ใช้ได้

🔹 ใช้สำหรับ จดจำสถานะของผู้ใช้ (เช่นล็อกอินอยู่หรือเปล่า?)
🔹 สามารถตั้งค่า หมดอายุ (Expire) ได้
🔹 มีการจำกัดขนาดข้อมูล (โดยทั่วไปไม่เกิน 4KB)

🔐 Session

Session คือการเก็บข้อมูลของผู้ใช้ ฝั่งเซิร์ฟเวอร์ และอ้างอิงด้วย Session ID ที่เก็บไว้ใน Cookie

🔹 ปลอดภัยกว่าการเก็บข้อมูลใน Cookie เพราะข้อมูลไม่ได้ถูกเก็บไว้ที่ฝั่งผู้ใช้
🔹 ต้องใช้ Session Storage เช่น Redis, Database หรือ Memory
🔹 มีอายุการใช้งานตามที่กำหนด (เช่น 30 นาที)

💡 โดยทั่วไป Session & Cookie ใช้งานร่วมกัน คือ เก็บ Session ID ไว้ใน Cookie เพื่อให้เซิร์ฟเวอร์ดึงข้อมูลผู้ใช้มาใช้ได้


🚀 วิธีใช้งาน Cookie ใน Next.js

📌 ติดตั้งไลบรารีจัดการ Cookie

npm install cookie


🔹 สร้าง API ที่ใช้ Cookie ใน Next.js

📌 โค้ด: pages/api/auth/set-cookie.js

import { serialize } from "cookie";

export default function handler(req, res) {
  const token = "my_secure_token"; // จำลอง token

  res.setHeader("Set-Cookie", serialize("auth_token", token, {
    httpOnly: true, // ป้องกันการเข้าถึงจาก JavaScript
    secure: process.env.NODE_ENV === "production", // ใช้ https ในโหมด production
    maxAge: 60 * 60 * 24, // 1 วัน
    sameSite: "strict",
    path: "/",
  }));

  res.json({ message: "Cookie set!" });
}

✅ API นี้จะสร้าง auth_token แล้วฝังไว้ใน Cookie
✅ ตั้งค่า httpOnly เพื่อให้ปลอดภัยจาก XSS
✅ หมดอายุใน 1 วัน

🔹 อ่านค่า Cookie

📌 โค้ด: pages/api/auth/get-cookie.js

import { parse } from "cookie";

export default function handler(req, res) {
  const cookies = parse(req.headers.cookie || "");
  res.json({ token: cookies.auth_token || "No token found" });
}

✅ API นี้จะอ่านค่า auth_token จาก Cookie

🔹 ลบ Cookie (Logout)

📌 โค้ด: pages/api/auth/logout.js

import { serialize } from "cookie";

export default function handler(req, res) {
  res.setHeader("Set-Cookie", serialize("auth_token", "", {
    httpOnly: true,
    secure: process.env.NODE_ENV === "production",
    expires: new Date(0), // ลบ Cookie
    path: "/",
  }));

  res.json({ message: "Logged out!" });
}

✅ API นี้จะลบ Cookie โดยตั้งค่า expire เป็น 0


🚀 วิธีใช้งาน Session ใน Next.js

📌 ติดตั้งไลบรารี Session

npm install next-session

next-session เป็นไลบรารีที่ช่วยจัดการ Session ใน Next.js API Routes ได้ง่าย

🔹 ตั้งค่า Session Middleware

📌 โค้ด: lib/session.js

import { withIronSessionApiRoute } from "iron-session/next";

export function withSession(handler) {
  return withIronSessionApiRoute(handler, {
    cookieName: "session_cookie",
    password: "complex_password_123", // ควรใช้ env variable
    cookieOptions: {
      secure: process.env.NODE_ENV === "production",
    },
  });
}

✅ ใช้ iron-session เก็บ Session อย่างปลอดภัย
✅ ตั้งค่า session_cookie เป็นที่เก็บ Session

🔹 สร้าง API ที่ใช้ Session

📌 โค้ด: pages/api/auth/login.js

import { withSession } from "../../lib/session";

export default withSession(async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  req.session.user = { id: 1, username: "admin" };
  await req.session.save();

  res.json({ message: "Login successful", user: req.session.user });
});

✅ เมื่อล็อกอินสำเร็จ จะเก็บข้อมูลผู้ใช้ใน Session

🔹 ดึงข้อมูลจาก Session

📌 โค้ด: pages/api/auth/me.js

import { withSession } from "../../lib/session";

export default withSession(async function handler(req, res) {
  if (!req.session.user) {
    return res.status(401).json({ error: "Not authenticated" });
  }

  res.json({ user: req.session.user });
});

✅ API นี้จะเช็กว่า มี Session ไหม? ถ้ามีแสดงข้อมูลผู้ใช้

🔹 ลบ Session (Logout)

📌 โค้ด: pages/api/auth/logout.js

import { withSession } from "../../lib/session";

export default withSession(async function handler(req, res) {
  req.session.destroy();
  res.json({ message: "Logged out!" });
});

✅ API นี้จะ ลบ Session แล้วทำให้ผู้ใช้ต้องล็อกอินใหม่

🎨 สร้างหน้า UI สำหรับ Login

📌 โค้ด: pages/index.js

import { useState } from "react";

export default function Home() {
  const [message, setMessage] = useState("");

  const login = async () => {
    const res = await fetch("/api/auth/login", { method: "POST" });
    const data = await res.json();
    setMessage(data.message);
  };

  const checkSession = async () => {
    const res = await fetch("/api/auth/me");
    const data = await res.json();
    setMessage(data.user ? `Logged in as ${data.user.username}` : "Not authenticated");
  };

  return (
    <div>
      <h1>Session & Cookie in Next.js</h1>
      <button onClick={login}>Login</button>
      <button onClick={checkSession}>Check Session</button>
      <p>{message}</p>
    </div>
  );
}

✅ UI นี้จะ Login แล้วเช็กว่า Session ยังอยู่ไหม?


🚀 ทดสอบระบบ

  1. รันเซิร์ฟเวอร์ npm run dev
  2. เปิด http://localhost:3000
  3. กด Login แล้วดูว่ามี Session หรือไม่


🎯 สรุป

✅ Cookie – ใช้เก็บข้อมูลเล็กๆ ฝั่ง client
✅ Session – ใช้เก็บข้อมูลฝั่งเซิร์ฟเวอร์
✅ Next.js API Routes รองรับทั้ง Cookie & Session
✅ ใช้ iron-session ทำให้การจัดการ Session ง่ายขึ้น

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

การสร้างระบบสมาชิก (Sign-up & Login) ใน Next.js แบบเข้าใจง่าย!

อยากทำระบบ สมัครสมาชิก (Sign-up) + ล็อกอิน (Login) ใน Next.js ใช่ไหม? 🤔 ไม่ต้องห่วง บทความนี้จะพาไปทำแบบ step-by-step ตั้งแต่สร้างบัญชี, เข้ารหัสรหัสผ่าน, จัดการ JWT Token, ยันป้องกัน API รับรองว่าอ่านจบแล้วทำเป็นแน่นอน!

🎯 แผนที่ที่เราจะทำ

✅ สมัครสมาชิก (Sign-up)
✅ เข้าสู่ระบบ (Login) พร้อมสร้าง JWT Token
✅ ป้องกัน API ด้วย Token
✅ แสดงข้อมูลผู้ใช้ที่ล็อกอินอยู่

💡 ใช้แค่ Next.js API Routes, bcrypt, และ JSON Web Token (JWT) ไม่ต้องมีเซิร์ฟเวอร์เพิ่ม!


🛠️ ติดตั้ง Next.js และไลบรารีที่จำเป็น

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

npx create-next-app@latest my-auth-app
cd my-auth-app
npm install bcryptjs jsonwebtoken

👉 bcryptjs – ใช้เข้ารหัสรหัสผ่าน
👉 jsonwebtoken – ใช้สร้างและตรวจสอบ JWT Token


🔐 สร้าง API สำหรับ สมัครสมาชิก (Sign-up)

📌 โค้ด: pages/api/auth/signup.js

import bcrypt from "bcryptjs";

let users = []; // จำลองเป็น Database (จริงๆ ควรใช้ MongoDB หรือ PostgreSQL)

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { username, password } = req.body;

  if (users.find((u) => u.username === username)) {
    return res.status(400).json({ error: "Username already exists" });
  }

  const hashedPassword = await bcrypt.hash(password, 10);
  const newUser = { id: Date.now(), username, password: hashedPassword };

  users.push(newUser);
  res.json({ message: "User registered", user: { id: newUser.id, username: newUser.username } });
}

🔹 เช็กว่าชื่อผู้ใช้ซ้ำหรือไม่
🔹 เข้ารหัสรหัสผ่านก่อนบันทึก


🔑 สร้าง API สำหรับ เข้าสู่ระบบ (Login)

📌 โค้ด: pages/api/auth/login.js

import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

let users = []; // จำลอง Database (ใช้ร่วมกับ signup.js)

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { username, password } = req.body;
  const user = users.find((u) => u.username === username);

  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ error: "Invalid credentials" });
  }

  const token = jwt.sign({ userId: user.id, username: user.username }, "SECRET_KEY", { expiresIn: "1h" });

  res.json({ message: "Login successful", token });
}

🔐 ถ้าล็อกอินสำเร็จ จะสร้าง JWT Token และส่งกลับไปให้ client


🛡️ สร้าง API ที่ต้องใช้ Token ในการเข้าถึง

📌 โค้ด: pages/api/auth/me.js

import jwt from "jsonwebtoken";

export default function handler(req, res) {
  const { authorization } = req.headers;
 
  if (!authorization) return res.status(401).json({ error: "No token provided" });

  try {
    const token = authorization.split(" ")[1];
    const decoded = jwt.verify(token, "SECRET_KEY");
    res.json({ message: "Authenticated", user: decoded });
  } catch (err) {
    res.status(401).json({ error: "Invalid token" });
  }
}

🔒 API นี้ต้องใช้ JWT Token ใน Authorization header เพื่อเข้าถึง


🎨 สร้างหน้า UI สำหรับ สมัครสมาชิก + ล็อกอิน

📌 โค้ด: pages/index.js

import { useState } from "react";

export default function Home() {
  const [username, setUsername] = useState("");
  const [password, setPassword] = useState("");
  const [token, setToken] = useState(null);
  const [message, setMessage] = useState("");

  const signup = async () => {
    const res = await fetch("/api/auth/signup", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password }),
    });

    const data = await res.json();
    setMessage(data.message);
  };

  const login = async () => {
    const res = await fetch("/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username, password }),
    });

    const data = await res.json();
    if (data.token) {
      setToken(data.token);
      setMessage("Login successful!");
    } else {
      setMessage("Login failed");
    }
  };

  const getUserData = async () => {
    const res = await fetch("/api/auth/me", {
      headers: { Authorization: `Bearer ${token}` },
    });

    const data = await res.json();
    setMessage(data.message || "Access denied");
  };

  return (
    <div>
      <h1>Sign-up & Login System</h1>
      <input type="text" placeholder="Username" onChange={(e) => setUsername(e.target.value)} />
      <input type="password" placeholder="Password" onChange={(e) => setPassword(e.target.value)} />
      <button onClick={signup}>Sign Up</button>
      <button onClick={login}>Login</button>
      <button onClick={getUserData} disabled={!token}>Get User Data</button>
      <p>{message}</p>
    </div>
  );
}

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

✅ กรอกชื่อผู้ใช้ + รหัสผ่าน แล้วกด Sign Up เพื่อสมัคร
✅ กด Login ถ้าสำเร็จจะได้ JWT Token
✅ กด Get User Data เพื่อเรียก API ที่ป้องกันไว้


🚀 ทดสอบระบบ

  1. รันเซิร์ฟเวอร์ npm run dev
  2. เปิด http://localhost:3000
  3. ลอง Sign-up > Login > กด Get User Data

ถ้า JWT Token ถูกต้อง API จะให้เข้าถึงข้อมูลได้! 🎉


🎯 สรุป

✅ Sign-up พร้อมเข้ารหัสรหัสผ่าน
✅ Login แล้วสร้าง JWT Token
✅ ป้องกัน API ด้วย JWT
✅ ใช้ Next.js API Routes ทำระบบ Authentication แบบ stateless

🚀 พร้อมใช้ในโปรเจกต์จริง เพิ่ม Database และ refresh token ได้เลย!

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

การใช้ JWT Authentication ใน Next.js แบบง่ายแต่โหด

ถ้าคุณกำลังทำเว็บแอปด้วย Next.js และอยากได้ระบบล็อกอินที่ปลอดภัยแต่ไม่ยุ่งยาก JWT Authentication คือคำตอบ! 😎

JWT (JSON Web Token) เป็นมาตรฐานสำหรับการส่งข้อมูลแบบ secure ระหว่าง client และ server โดยไม่ต้องใช้ session บนเซิร์ฟเวอร์ให้ยุ่งยาก ซึ่งเหมาะกับแอปที่ต้องการ stateless authentication เช่น Next.js API Routes หรือ Serverless Functions

บทความนี้จะพาไปดูวิธีใช้งาน JWT Authentication ใน Next.js ตั้งแต่เริ่มต้น ยันการป้องกัน API ให้ปลอดภัย 💪

🎯 JWT คืออะไร?

JWT (JSON Web Token) เป็นโทเค็นเข้ารหัสที่ใช้แทน session ในการตรวจสอบตัวตน (Authentication) ของผู้ใช้ โดยมันจะถูกฝังข้อมูล เช่น user ID, role, email ไว้ข้างใน และสามารถใช้ยืนยันตัวตนในทุกๆ Request ได้

JWT มี 3 ส่วนหลักๆ คือ

  • Header – บอกว่าใช้การเข้ารหัสแบบไหน
  • Payload – ข้อมูลของผู้ใช้ เช่น ID, ชื่อ, อีเมล
  • Signature – ใช้สำหรับยืนยันว่าโทเค็นถูกต้อง


ตัวอย่าง JWT แบบดิบๆ 👇

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOjEsImlhdCI6MTYxNjIzOTAyMn0.PF-0Ff_q2Gsk2tKAlDhEwMzMfJvc_ZjZbnF0qU4aMSk

ลองเอาไป Decode ได้ที่ jwt.io 😃



🛠️ ติดตั้ง Next.js และ JWT

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

npx create-next-app@latest jwt-auth-app
cd jwt-auth-app
npm install jsonwebtoken bcryptjs

👉 jsonwebtoken – ใช้สำหรับสร้างและตรวจสอบ JWT
👉 bcryptjs – ใช้เข้ารหัสรหัสผ่าน


🔐 สร้าง API สำหรับ JWT Authentication

1️⃣ สร้าง API สำหรับ ล็อกอิน และสร้าง JWT

ไปที่ pages/api/auth/login.js แล้วใส่โค้ดนี้

import jwt from "jsonwebtoken";
import bcrypt from "bcryptjs";

// ข้อมูลผู้ใช้ตัวอย่าง (จริงๆ ควรใช้ DB)
const users = [{ id: 1, username: "admin", password: "$2a$10$KIX/b0WwQJHsc/mxL6RoGuLbqVZXuWEC1Q2tB3MTzK8K4NrdH5r3W" }]; // รหัสผ่าน: 123456

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { username, password } = req.body;
  const user = users.find((u) => u.username === username);

  if (!user || !(await bcrypt.compare(password, user.password))) {
    return res.status(401).json({ error: "Invalid credentials" });
  }

  const token = jwt.sign({ userId: user.id, username: user.username }, "SECRET_KEY", { expiresIn: "1h" });

  res.json({ token });
}

🔹 เราใช้ bcryptjs เพื่อตรวจสอบรหัสผ่าน
🔹 ถ้าล็อกอินสำเร็จ จะสร้าง JWT Token แล้วส่งกลับไปให้ client

2️⃣ สร้าง API สำหรับ ป้องกัน API ด้วย JWT

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

import jwt from "jsonwebtoken";

export default function handler(req, res) {
  const { authorization } = req.headers;
 
  if (!authorization) return res.status(401).json({ error: "No token provided" });

  try {
    const token = authorization.split(" ")[1];
    const decoded = jwt.verify(token, "SECRET_KEY");
    res.json({ message: "Protected data", user: decoded });
  } catch (err) {
    res.status(401).json({ error: "Invalid token" });
  }
}

🔒 API นี้จะตรวจสอบว่า

✅ มี JWT Token ใน Authorization header ไหม
✅ ถอดรหัสแล้วถูกต้องหรือไม่

3️⃣ สร้าง API สำหรับ สมัครสมาชิก

สร้างไฟล์ pages/api/auth/register.js

import bcrypt from "bcryptjs";

export default async function handler(req, res) {
  if (req.method !== "POST") return res.status(405).json({ error: "Method Not Allowed" });

  const { username, password } = req.body;
  const hashedPassword = await bcrypt.hash(password, 10);

  // จำลองการบันทึกลง DB
  const newUser = { id: Date.now(), username, password: hashedPassword };

  res.json({ message: "User registered", user: newUser });
}

🔹 ใช้ bcryptjs เพื่อเข้ารหัสรหัสผ่านก่อนบันทึก
🔹 จำลองว่าเราเก็บข้อมูลผู้ใช้ใน DB


🎨 สร้าง UI สำหรับล็อกอิน

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

import { useState } from "react";

export default function Home() {
  const [token, setToken] = useState(null);
  const [message, setMessage] = useState("");

  const login = async () => {
    const res = await fetch("/api/auth/login", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ username: "admin", password: "123456" }),
    });

    const data = await res.json();
    if (data.token) {
      setToken(data.token);
      setMessage("Login successful!");
    } else {
      setMessage("Login failed");
    }
  };

  const accessProtected = async () => {
    const res = await fetch("/api/protected", {
      headers: { Authorization: `Bearer ${token}` },
    });

    const data = await res.json();
    setMessage(data.message || "Access denied");
  };

  return (
    <div>
      <h1>JWT Authentication in Next.js</h1>
      <button onClick={login}>Login</button>
      <button onClick={accessProtected} disabled={!token}>Access Protected API</button>
      <p>{message}</p>
    </div>
  );
}

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

  • ปุ่ม Login จะส่ง Username และ Password ไปที่ API
  • ถ้าล็อกอินสำเร็จจะได้ JWT Token มาเก็บไว้
  • ปุ่ม Access Protected API จะส่ง JWT Token ไปที่ API ที่ต้องการป้องกัน


🚀 ทดสอบระบบ JWT Authentication

  1.     รันเซิร์ฟเวอร์ npm run dev
  2.     เปิด http://localhost:3000
  3.     กดปุ่ม "Login"
  4.     กดปุ่ม "Access Protected API" เพื่อดูว่ามีสิทธิ์เข้าถึงหรือไม่


🎯 สรุป

✅ JWT Authentication ทำให้ระบบล็อกอินเป็นแบบ stateless และปลอดภัย
✅ ไม่ต้องใช้ session storage บนเซิร์ฟเวอร์
✅ ใช้งานง่ายกับ Next.js API Routes

แค่นี้ก็ได้ระบบล็อกอินเทพๆ ไปใช้ในแอป Next.js แล้ว! 🔥

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

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