Written & drawn bycreaml4tt3© 2026

Home/Blogs/วิธีการทำ Drag to Swap หรือ การลากและวางใน React และ Next.js ด้วย Swapy

วิธีการทำ Drag to Swap หรือ การลากและวางใน React และ Next.js ด้วย Swapy

Published 06 Mar 2025Updated 28 Mar 2025
วิธีการทำ Drag to Swap หรือ การลากและวางใน React และ Next.js ด้วย Swapy

In short

Drag to Swap คือเทคนิคการลากและวาง element ที่ช่วยให้ผู้ใช้สามารถจัดเรียงหรือสลับตำแหน่งของรายการได้อย่างสะดวก มาเรียนรู้วิธีการทำ Drag to Swap บน React หรือ Next.js ด้วย Swapy

On this page

1 sections
  1. 01เมื่อการออกแบบ UI กลายเป็นเรื่องง่ายด้วย Drag to Swap
    1. ขั้นตอนที่ 1: Install Swapy เข้า Project ของเรา
    2. ขั้นตอนที่ 2: Import Dependencies ในไฟล์ที่เราต้องการใช้งาน
    3. ขั้นตอนที่ 3: ประกาศตัวแปร useRef สำหรับ container และ ตัวเก็บค่า state
    4. ขั้นตอนที่ 4: สร้าง swapy instance ใน useEffect
    5. ขั้นตอนที่ 5: return div container, slot และ item ด้วย slot คือ data-swapy-slot และ item คือ data-swapy-item
    6. code เต็ม
    7. ขั้นตอนที่ 1: Install Swapy เข้า Project ของเรา
    8. ขั้นตอนที่ 2: Import Dependencies ในไฟล์ที่เราต้องการใช้งาน
    9. ขั้นตอนที่ 3: ประกาศตัวแปร useRef สำหรับ container และ ตัวเก็บค่า state
    10. ขั้นตอนที่ 4: สร้าง swapy instance ใน useEffect
    11. ขั้นตอนที่ 5: return div container, slot และ item ด้วย slot คือ data-swapy-slot และ item คือ data-swapy-item
    12. code เต็ม

เมื่อการออกแบบ UI กลายเป็นเรื่องง่ายด้วย Drag to Swap

เคยรู้สึกไหมว่าการจัดเรียงข้อมูลหรือองค์ประกอบบนเว็บไซต์และแอปพลิเคชันเป็นเรื่องยุ่งยาก? วันนี้เราจะมาพาคุณสำรวจเทคนิคสุดล้ำที่จะเปลี่ยนวิธีคิดในการออกแบบอินเตอร์เฟซของคุณ - นั่นก็คือ "Drag to Swap"

เทคนิคนี้ไม่เพียงแต่ทำให้การใช้งานง่ายขึ้น แต่ยังเพิ่มประสบการณ์ผู้ใช้งาน ช่วยเพิ่ม interaction มากขึ้น ด้วยวันนี้เราจะมาแนะนำ dependency ตัวหนึ่งนั้นก็คือ Swapy สามารถใช้ได้ทั้ง

  • React

  • Vue

  • Svelte

โดยในวันนี้จะมาแสดงวิธีการง่ายๆ สำหรับการทำ Drag to Swap บน React (Next.js) กันครับ

อ่านเพิ่มเติมได้ที่

https://swapy.tahazsh.com/docs/installation/

ขั้นตอนที่ 1: Install Swapy เข้า Project ของเรา

npm install swapy
หรือ
<script src="https://unpkg.com/swapy/dist/swapy.min.js"></script>

ขั้นตอนที่ 2: Import Dependencies ในไฟล์ที่เราต้องการใช้งาน

// สำหรับใครที่ใช้ Next.js ต้องเพื่ม "use client"; ด้วย เพราะทำงานบน client
import { useEffect, useRef } from "react";
import { createSwapy } from "swapy";
import type {
  Swapy,
  BeforeSwapEvent,
  SwapStartEvent,
  SwapEvent,
  SwapEndEvent,
} from "swapy";

ขั้นตอนที่ 3: ประกาศตัวแปร useRef สำหรับ container และ ตัวเก็บค่า state

const App: React.FC = () => {
  const swapy = useRef<Swapy | null>(null); // สำหรับการเก็บ swapy instance
  const container = useRef<HTMLDivElement | null>(null); // สำหรับการเก็บ container ของ swapy

ขั้นตอนที่ 4: สร้าง swapy instance ใน useEffect

useEffect(() => {
    // เช็คก่อนว่า container โหลดแล้ว
    if (container.current) {
      swapy.current = createSwapy(container.current);


      // event ของ swapy ก่อนจะลาก จำเป็นต้อง return boolean false คือลากไม่ได้ true คือลากได้
      swapy.current.onBeforeSwap((event: BeforeSwapEvent): boolean => {
        console.log("swap onBeforeSwap", event);
        return true;
      });
      // event ของ swapy เมื่อเริ่มลาก
      swapy.current.onSwapStart((event: SwapStartEvent): void => {
        console.log("swap onSwapStart", event);
      });
      // event ของ swapy ขณะลาก
      swapy.current.onSwap((event: SwapEvent): void => {
        console.log("swap onSwap", event);
      });
      // event ของ swapy เมื่อลากเสร็จ
      swapy.current.onSwapEnd((event: SwapEndEvent): void => {
        console.log("swap onSwapEnd", event);
      });
    }


    return () => {
      // เคลียร์ swapy
      swapy.current?.destroy();
    };
  }, []);

ขั้นตอนที่ 5: return div container, slot และ item ด้วย slot คือ data-swapy-slot และ item คือ data-swapy-item

return (
    <div ref={container}>


      {/* slot a */}
      <div data-swapy-slot="a">
        {/* item a */}
        <div data-swapy-item="a">
          <div>A</div>
        </div>
      </div>


      {/* slot b */}
      <div data-swapy-slot="b">
        {/* item a */}
        <div data-swapy-item="b">
          <div>B</div>
        </div>
      </div>
    </div>
  );

code เต็ม

import { useEffect, useRef } from "react";
import { createSwapy } from "swapy";
import type {
  Swapy,
  BeforeSwapEvent,
  SwapStartEvent,
  SwapEvent,
  SwapEndEvent,
} from "swapy";


const App: React.FC = () => {
  const swapy = useRef<Swapy | null>(null); // สำหรับการเก็บ swapy instance
  const container = useRef<HTMLDivElement | null>(null); // สำหรับการเก็บ container ของ swapy


  useEffect(() => {
    // เช็คก่อนว่า container โหลดแล้ว
    if (container.current) {
      swapy.current = createSwapy(container.current, {}); // สามารถใส่ options เพิ่มเติมได้เช็คได้ที่ docs


      // event ของ swapy ก่อนจะลาก จำเป็นต้อง return boolean false คือลากไม่ได้ true คือลากได้
      swapy.current.onBeforeSwap((event: BeforeSwapEvent): boolean => {
        console.log("swap onBeforeSwap", event);
        return true;
      });
      // event ของ swapy เมื่อเริ่มลาก
      swapy.current.onSwapStart((event: SwapStartEvent): void => {
        console.log("swap onSwapStart", event);
      });
      // event ของ swapy ขณะลาก
      swapy.current.onSwap((event: SwapEvent): void => {
        console.log("swap onSwap", event);
      });
      // event ของ swapy เมื่อลากเสร็จ
      swapy.current.onSwapEnd((event: SwapEndEvent): void => {
        console.log("swap onSwapEnd", event);
      });
    }


    return () => {
      // เคลียร์ swapy
      swapy.current?.destroy();
    };
  }, []);


  return (
    <div ref={container}>


      {/* slot a */}
      <div data-swapy-slot="a">
        {/* item a */}
        <div data-swapy-item="a">
          <div>A</div>
        </div>
      </div>


      {/* slot b */}
      <div data-swapy-slot="b">
        {/* item a */}
        <div data-swapy-item="b">
          <div>B</div>
        </div>
      </div>
    </div>
  );
};


export default App;

และยังมีอีกวิธีสำหรับคนที่ต้องการความ dynamic ให้เนื้อหาภายในมีการ update ตลอดเวลา

ขั้นตอนที่ 1: Install Swapy เข้า Project ของเรา

npm install swapy
หรือ
<script src="https://unpkg.com/swapy/dist/swapy.min.js"></script>

ขั้นตอนที่ 2: Import Dependencies ในไฟล์ที่เราต้องการใช้งาน

// สำหรับใครที่ใช้ Next.js ต้องเพื่ม "use client"; ด้วย เพราะทำงานบน client
import { useEffect, useRef, useState, useMemo } from "react";
import { createSwapy, utils } from "swapy";
import type { Swapy, SlotItemMapArray, SwapEvent } from "swapy";


type Item = {
  id: string; // สำหรับเก็บ id ของ item และเป็น key ใน slotItemMap
  title: string; // สำหรับเก็บชื่อ item
};


const initialItems: Item[] = [
  { id: "1", title: "1" },
  { id: "2", title: "2" },
  { id: "3", title: "3" },
];


let id = 4;

ขั้นตอนที่ 3: ประกาศตัวแปร useRef สำหรับ container และ ตัวเก็บค่า state

const swapy = useRef<Swapy | null>(null); // สำหรับการเก็บ swapy instance
  const container = useRef<HTMLDivElement | null>(null); // สำหรับการเก็บ container ของ swapy
  const [items, setItems] = useState<Item[]>(initialItems); // สำหรับเก็บ items ที่จะใช้ใน swapy
  const [slotItemMap, setSlotItemMap] = useState<SlotItemMapArray>(
    utils.initSlotItemMap(items, "id"), // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
  );
  const slottedItems = useMemo(
    () => utils.toSlottedItems(items, "id", slotItemMap), // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
    [items, slotItemMap],
  );


  // ทุกครั้งที่ items เปลี่ยนแปลงจะทำการเรียกใช้งาน dynamicSwapy เพื่อให้ swapy ทำการเรียงลำดับ item ใหม่
  useEffect(
    () =>
      utils.dynamicSwapy(
        swapy.current,
        items,
        "id", // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
        slotItemMap,
        setSlotItemMap,
      ),
    [items],
  );

ขั้นตอนที่ 4: สร้าง swapy instance ใน useEffect

useEffect(() => {
    // เช็คก่อนว่า container โหลดแล้ว
    if (container.current) {
      swapy.current = createSwapy(container.current, {}); // สามารถใส่ options เพิ่มเติมได้เช็คได้ที่ docs


      // event ของ swapy ขณะลาก ให้ทำการเปลี่ยน slotItemMap ตามที่ลาก
      swapy.current.onSwap((event: SwapEvent): void => {
        setSlotItemMap(event.newSlotItemMap.asArray);
      });


      return () => {
        // เคลียร์ swapy
        swapy.current?.destroy();
      };
    }
  }, []);

ขั้นตอนที่ 5: return div container, slot และ item ด้วย slot คือ data-swapy-slot และ item คือ data-swapy-item

return (
    <div className="container" ref={container}>
      <div className="items">
        {/* แสดง items ที่สามารถลาก */}
        {slottedItems.map(({ slotId, itemId, item }, index) => (
          <div className="slot" key={slotId} data-swapy-slot={slotId}>
            {item && (
              <div className="item" data-swapy-item={itemId} key={itemId}>
                <span>{index + 1}</span>
                <span>{item.title}</span>
                {/* ปุ่มลบ item */}
                <span
                  className="delete"
                  data-swapy-no-drag
                  onClick={() => {
                    setItems(items.filter((i) => i.id !== item.id));
                  }}
                ></span>
              </div>
            )}
          </div>
        ))}
      </div>


      {/* ปุ่มเพิ่ม item */}
      <div
        className="item item--add"
        onClick={() => {
          const newItem: Item = { id: `${id}`, title: `${id}` };
          setItems([...items, newItem]);
          id++;
        }}
      >
        +
      </div>
    </div>
  );

code เต็ม

import { useEffect, useRef, useState, useMemo } from "react";
import { createSwapy, utils } from "swapy";
import type { Swapy, SlotItemMapArray, SwapEvent } from "swapy";


type Item = {
  id: string; // สำหรับเก็บ id ของ item และเป็น key ใน slotItemMap
  title: string; // สำหรับเก็บชื่อ item
};


const initialItems: Item[] = [
  { id: "1", title: "1" },
  { id: "2", title: "2" },
  { id: "3", title: "3" },
];


let id = 4;


const AppDynamic: React.FC = () => {
  const swapy = useRef<Swapy | null>(null); // สำหรับการเก็บ swapy instance
  const container = useRef<HTMLDivElement | null>(null); // สำหรับการเก็บ container ของ swapy
  const [items, setItems] = useState<Item[]>(initialItems); // สำหรับเก็บ items ที่จะใช้ใน swapy
  const [slotItemMap, setSlotItemMap] = useState<SlotItemMapArray>(
    utils.initSlotItemMap(items, "id"), // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
  );
  const slottedItems = useMemo(
    () => utils.toSlottedItems(items, "id", slotItemMap), // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
    [items, slotItemMap],
  );


  // ทุกครั้งที่ items เปลี่ยนแปลงจะทำการเรียกใช้งาน dynamicSwapy เพื่อให้ swapy ทำการเรียงลำดับ item ใหม่
  useEffect(
    () =>
      utils.dynamicSwapy(
        swapy.current,
        items,
        "id", // "id" ต้องเป็น string เท่านั่นเป็นตัวเลขไม่ได้ (v.1.0.5)
        slotItemMap,
        setSlotItemMap,
      ),
    [items],
  );


  useEffect(() => {
    // เช็คก่อนว่า container โหลดแล้ว
    if (container.current) {
      swapy.current = createSwapy(container.current, {}); // สามารถใส่ options เพิ่มเติมได้เช็คได้ที่ docs


      // event ของ swapy ขณะลาก ให้ทำการเปลี่ยน slotItemMap ตามที่ลาก
      swapy.current.onSwap((event: SwapEvent): void => {
        setSlotItemMap(event.newSlotItemMap.asArray);
      });


      return () => {
        // เคลียร์ swapy
        swapy.current?.destroy();
      };
    }
  }, []);


  return (
    <div className="container" ref={container}>
      <div className="items">
        {/* แสดง items ที่สามารถลาก */}
        {slottedItems.map(({ slotId, itemId, item }, index) => (
          <div className="slot" key={slotId} data-swapy-slot={slotId}>
            {item && (
              <div className="item" data-swapy-item={itemId} key={itemId}>
                <span>{index + 1}</span>
                <span>{item.title}</span>
                {/* ปุ่มลบ item */}
                <span
                  className="delete"
                  data-swapy-no-drag
                  onClick={() => {
                    setItems(items.filter((i) => i.id !== item.id));
                  }}
                ></span>
              </div>
            )}
          </div>
        ))}
      </div>


      {/* ปุ่มเพิ่ม item */}
      <div
        className="item item--add"
        onClick={() => {
          const newItem: Item = { id: `${id}`, title: `${id}` };
          setItems([...items, newItem]);
          id++;
        }}
      >
        +
      </div>
    </div>
  );
};


export default AppDynamic;
#Next.js#React#Swapy#Drag to Swap#วิธีการทำ Drag to Swap#Typescript
Teekayu Poongkawabutr (Pon)
Teekayu Poongkawabutr (Pon)(Creaml4tt3)

Developer ท่านหนึ่งที่ทำงานบ้าง ไม่ทำงานบ้าง ทำงานเป็น dev แต่จบ graphic ชีวิตสับสน website: creaml4tt3.me

Further Reading

Related Blogs

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

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

ทำให้ Next.js ของคุณลื่นเหมือน native app ด้วย View Transitions API พร้อมวิธีเปิดใช้งาน, ทำ global transition และทำ shared element animation สำหรับ blog image แบบ production-ready

03 May 2026

More Field Notes

Latest Blogs

View all
Google Workspace MCP ขึ้น The caller does not have permission? สาเหตุและวิธีแก้ใน Claude CodePersonal

Google Workspace MCP ขึ้น The caller does not have permission? สาเหตุและวิธีแก้ใน Claude Code

ต่อ Claude Code กับ MCP ของ Google Drive, Sheets หรือ Slides แล้วขึ้น The caller does not have permission? สาเหตุหลักคือยังไม่ได้สมัคร Developer Preview นี่คือสิ่งที่ต้องมีและขั้นตอนแบบย่อ

23 Oct 2026
Twintail คืออะไร? สร้าง Persona Skill ซึนเดเระให้ Claude Code กับ Codex และบทเรียนจากการวัดผลจริงPersonal

Twintail คืออะไร? สร้าง Persona Skill ซึนเดเระให้ Claude Code กับ Codex และบทเรียนจากการวัดผลจริง

Twintail คือ skill บุคลิกซึนเดเระแบบโอเพนซอร์สสำหรับ Claude Code และ Codex ติดตั้งคำสั่งเดียว พร้อมบทเรียนจากการทำ eval ว่าทำ persona ยังไงไม่ให้คำตอบแย่ลง

22 Oct 2026
9Router คืออะไร? รวม Claude Code, Codex และ AI ฟรีไว้ endpoint เดียว ให้โค้ดต่อได้ไม่สะดุดPersonal

9Router คืออะไร? รวม Claude Code, Codex และ AI ฟรีไว้ endpoint เดียว ให้โค้ดต่อได้ไม่สะดุด

ใช้ 9Router ทำ local proxy ให้ Claude Code, Codex และ Cursor สลับไปใช้ provider สำรองอัตโนมัติเมื่อโควตาหมด พร้อมวิธีติดตั้ง ตั้งค่า และเรื่องความปลอดภัยที่ต้องรู้

16 Oct 2026