npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

thaizip

v0.4.0

Published

Fast, fuzzy Thai address autocomplete library (headless + React)

Readme

thaizip

ไลบรารี autocomplete ที่อยู่ไทยแบบ fuzzy search รวดเร็ว รองรับทั้ง Vanilla JS และ React

  • ค้นหาได้ทั้งภาษาไทย ภาษาอังกฤษ และรหัสไปรษณีย์
  • Fuzzy search — ไม่ต้องใส่วรรณยุกต์ก็ค้นหาได้ เช่น "ลาดพราว" เจอ "ลาดพร้าว"
  • ไม่มี dependency นอกจาก React (optional)
  • โหลดข้อมูลแบบ async — ไม่บล็อก initial bundle (~132 KB gzip)
  • รองรับ ESM และ CJS

ติดตั้ง

npm install thaizip

ถ้าใช้กับ React ต้องมี React >= 18 เป็น peer dependency อยู่แล้ว ไม่ต้องติดตั้งเพิ่ม


การใช้งานพื้นฐาน (Vanilla JS / TypeScript)

โหลด index และค้นหาที่อยู่

import { loadDefaultIndex } from 'thaizip/data'
import { searchThaiAddress } from 'thaizip'

// โหลด index ครั้งแรก ~200ms (cached หลังจากนั้น)
const index = await loadDefaultIndex()

// ค้นหาด้วยชื่อตำบล/อำเภอ/จังหวัด
const results = searchThaiAddress(index, 'ลาดพร้าว')

// ค้นหาด้วยรหัสไปรษณีย์
const results2 = searchThaiAddress(index, '10900')

// ค้นหาด้วยภาษาอังกฤษ
const results3 = searchThaiAddress(index, 'chiang mai')

// ⚠️ ไม่รองรับ: ค้นหาแบบรวม text + รหัสไปรษณีย์ในครั้งเดียว (เช่น "ลาดพร้าว 10900")
// ให้ค้นหาด้วยชื่อสถานที่ หรือรหัสไปรษณีย์ แยกกัน

console.log(results)
// [{ tambonId, tambonNameTh, tambonNameEn, amphureId, amphureNameTh, ... zipCode }, ...]

ตัวเลือก (options)

const results = searchThaiAddress(index, 'ลาดพร้าว', {
  limit: 5,       // จำนวนผลลัพธ์สูงสุด (default: 10)
  threshold: 0.4, // ความแม่นยำขั้นต่ำ 0–1 (default: 0.4)
})

แปลงผลลัพธ์เป็นรูปแบบต่าง ๆ

formatThaiAddressSuggestion — ใช้แสดงใน dropdown

import { formatThaiAddressSuggestion } from 'thaizip'

const suggestion = formatThaiAddressSuggestion(results[0])
// {
//   id: '100101',
//   label: 'ลาดพร้าว > ลาดพร้าว > กรุงเทพมหานคร 10230',
//   tambon: 'ลาดพร้าว',     tambonEn: 'Lat Phrao',
//   amphure: 'ลาดพร้าว',    amphureEn: 'Lat Phrao',
//   province: 'กรุงเทพมหานคร', provinceEn: 'Bangkok',
//   zipCode: '10230',
// }

resolveThaiAddress — ใช้บันทึกข้อมูลหลังผู้ใช้เลือก

import { resolveThaiAddress } from 'thaizip'

const resolved = resolveThaiAddress(results[0])
// {
//   tambon: 'ลาดพร้าว',        tambonEn: 'Lat Phrao',
//   amphure: 'ลาดพร้าว',       amphureEn: 'Lat Phrao',
//   province: 'กรุงเทพมหานคร', provinceEn: 'Bangkok',
//   zipCode: '10230',
//   subdistrict: 'ลาดพร้าว',   subdistrictEn: 'Lat Phrao',
//   district: 'ลาดพร้าว',      districtEn: 'Lat Phrao',
//   postalCode: '10230',
// }

การใช้งานกับ React

useThaiAddressAutocomplete

Hook นี้จัดการ state ของ query, suggestions, debounce ให้ทั้งหมด

import { useState, useEffect } from 'react'
import { loadDefaultIndex } from 'thaizip/data'
import { useThaiAddressAutocomplete } from 'thaizip'
import type { TrigramIndex } from 'thaizip'

// แนะนำ: แยก loading และ form ออกจากกัน เพื่อให้ hook เรียกหลัง index พร้อมแล้วเสมอ
function AddressPage() {
  const [index, setIndex] = useState<TrigramIndex | null>(null)

  useEffect(() => {
    loadDefaultIndex().then(setIndex)
  }, [])

  if (!index) return <p>กำลังโหลด...</p>
  return <AddressForm index={index} />
}

function AddressForm({ index }: { index: TrigramIndex }) {
  const { query, setQuery, suggestions, isOpen, selectSuggestion, clear } =
    useThaiAddressAutocomplete({
      index,
      limit: 10,       // default: 10
      debounce: 200,   // default: 200ms
      threshold: 0.4,  // default: 0.4
    })

  const [address, setAddress] = useState(null)

  return (
    <div>
      <input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder="พิมพ์ตำบล อำเภอ จังหวัด หรือรหัสไปรษณีย์"
      />

      {isOpen && (
        <ul>
          {suggestions.map((s) => (
            <li key={s.id} onClick={() => {
              const resolved = selectSuggestion(s)
              setAddress(resolved)
            }}>
              {s.label}
            </li>
          ))}
        </ul>
      )}

      {address && (
        <div>
          <p>ตำบล: {address.tambon} ({address.tambonEn})</p>
          <p>อำเภอ: {address.amphure} ({address.amphureEn})</p>
          <p>จังหวัด: {address.province} ({address.provinceEn})</p>
          <p>รหัสไปรษณีย์: {address.zipCode}</p>
        </div>
      )}
    </div>
  )
}

Return ของ hook

| ค่า | ประเภท | คำอธิบาย | |---|---|---| | query | string | ข้อความที่ผู้ใช้พิมพ์อยู่ | | setQuery | (value: string) => void | อัปเดต query | | suggestions | ThaiAddressSuggestion[] | รายการผลลัพธ์ที่แสดงใน dropdown | | isOpen | boolean | true เมื่อมี query และมี suggestions | | selectSuggestion | (item) => ResolvedThaiAddress | เลือก suggestion แล้วได้ข้อมูลที่อยู่ครบ | | clear | () => void | ล้าง query และ suggestions ทั้งหมด |


การใช้งานกับ Node.js / Express

import express from 'express'
import { loadDefaultIndex } from 'thaizip/data'
import { searchThaiAddress, formatThaiAddressSuggestion } from 'thaizip'

const app = express()

// โหลด index ครั้งเดียวตอน startup
const index = await loadDefaultIndex()

app.get('/address/search', (req, res) => {
  const query = String(req.query.q ?? '')
  if (!query) return res.json([])

  const results = searchThaiAddress(index, query, { limit: 10 })
  res.json(results.map(formatThaiAddressSuggestion))
})

app.listen(3000)

เรียกใช้งาน:

GET /address/search?q=ลาดพร้าว
GET /address/search?q=10900

ใช้ข้อมูล Index ของตัวเอง

หากต้องการสร้าง index จากข้อมูลที่กำหนดเอง

import { buildThaiAddressIndex } from 'thaizip'

const index = buildThaiAddressIndex({
  provinces: [...],
  amphures: [...],
  tambons: [...],
  // geographies ไม่จำเป็นต้องระบุ (ไม่ได้ใช้งานโดย indexer)
})

รูปแบบข้อมูล raw ดูได้จาก type RawData, RawProvince, RawAmphure, RawTambon ที่ export มาจาก library


Types หลัก

type ThaiAddressSuggestion = {
  id: string
  label: string        // "ตำบล > อำเภอ > จังหวัด XXXXX"
  tambon: string
  tambonEn: string
  amphure: string
  amphureEn: string
  province: string
  provinceEn: string
  zipCode: string
}

type ResolvedThaiAddress = {
  tambon: string        // alias: subdistrict
  tambonEn: string      // alias: subdistrictEn
  amphure: string       // alias: district
  amphureEn: string     // alias: districtEn
  province: string
  provinceEn: string
  zipCode: string       // alias: postalCode
  subdistrict: string
  subdistrictEn: string
  district: string
  districtEn: string
  postalCode: string
}

Migration จาก v0.2.x → v0.3.0+

// v0.2.x (sync, bundle ~630KB)
import { defaultIndex } from 'thaizip/data'
const results = searchThaiAddress(defaultIndex, query)

// v0.3.0+ (async, bundle ~132KB gzip)
import { loadDefaultIndex } from 'thaizip/data'
const index = await loadDefaultIndex()  // cache อัตโนมัติ หลังจากโหลดครั้งแรก
const results = searchThaiAddress(index, query)

ข้อมูลที่อยู่

ข้อมูลอ้างอิงจากการแบ่งเขตการปกครองของไทย (77 จังหวัด, 920 อำเภอ, ~7,385 ตำบล)

ตำบลที่ยังมีสถานะ active แต่อ้างอิงอำเภอที่ถูก soft-deleted จะถูกข้ามโดย index โดยอัตโนมัติ พร้อมแสดง console.warn ให้ทราบ


License

MIT