April 2026
Yubinbango Demo
A Nuxt 3 demo of Japanese postal-code auto-fill patterns (yubin bango 郵便番号 + auto-kana), deployed to Cloudflare Workers via Wrangler with GitHub Actions auto-deploy on push to main.

Japanese postal-code auto-fill, demoed on Cloudflare.
Japanese postal-code auto-fill, demoed on Cloudflare.
Japanese address forms all solve the same small problem: a person types 7 digits, and prefecture, city, and town should fill themselves in. There are several libraries for it, most of them old, and the READMEs do not tell you which one survives a modern build. So I built all four and put them on one page.
Live at yubinbango.ujikaji.my. Each approach has its own working form, so the comparison is something you can type into rather than read about.
The four approaches
| Approach | Install | How it fetches | Verdict |
|---|---|---|---|
yubinbango-core | npm | JSONP | Needs a Vite transform to build |
yubinbango-core2 | npm | JSONP | Imports cleanly, then breaks on Cloudflare |
| zipcloud API | none | REST via $fetch | No bundler question at all; a network call you own |
yubinbango.js | CDN script tag | JSONP | Counter-example — do not use with Vue |
The one line that separates core from core2
They are the same library. Same JSONP data source, same Core(zip, cb) interface, near-identical implementation. The difference is at the very end of the compiled file:
// yubinbango-core (10 years old)
})(YubinBango || (YubinBango = {}));
// no module.exports → global variable only
// Vite cannot import this as-is
// yubinbango-core2 (7 years old)
})(YubinBango || (YubinBango = {}));
module.exports = YubinBango;
// CommonJS export → imports work locally
That is the entire reason core2 exists: you can import it without fighting your bundler.
Then you deploy it
/yubinbango-core2 and /composable returned 500, with module is not defined.
The cause is that same exported line. module.exports leaked into browser-executed runtime code in the Cloudflare bundle path, and in a browser runtime there is no module global to assign to. The one line that made core2 pleasant locally is the one line the edge cannot run.
The fix, in nuxt.config.ts, is a Vite transform that takes it back out:
if (id.includes('/yubinbango-core2/') && id.includes('yubinbango-core.js')) {
const withoutCommonJs = code.replace(/module\.exports\s*=\s*YubinBango;?/g, '')
return `${withoutCommonJs}\nexport default YubinBango;\n`
}
if (id.includes('/yubinbango-core/') && id.includes('yubinbango-core.js')) {
return `${code}\nexport default YubinBango;\n`
}
Which is the actual finding. You pick core2 over core specifically to avoid writing a Vite plugin, and then you write a Vite plugin anyway — to delete the feature you upgraded for. Worse, the same transform handles both packages; the two branches differ by whether a line is stripped first. Once it exists, the choice between core and core2 stops mattering for deployment at all.
What I would actually reach for
usePostalCode, the composable wrapping core2 — but for a different reason than the comparison suggests. Not because core2 is better, since the transform flattens that difference. Because it puts an unmaintained library behind one seam. Last release was 10 years ago for core and 7 for core2, and both need the network for JSONP. When one finally has to be replaced, a composable is a single file to rewrite instead of every form in the codebase.
If I were starting a project today and did not need offline behaviour, zipcloud is worth a hard look purely for having no bundler story to get wrong.
yubinbango.js stays on the page as the shape to avoid. It drives the form by DOM class names (p-postal-code, p-region), which is a reasonable design for a jQuery-era page and a bad one inside Vue: it writes to the DOM behind the framework's back, and only works at all behind a setTimeout. Keeping a wrong answer visible next to the right ones is more useful than deleting it.
Try it
| Postal code | Fills in |
|---|---|
1000001 | Tokyo, Chiyoda-ku, Chiyoda |
1600022 | Tokyo, Shinjuku-ku, Shinjuku |
5300001 | Osaka, Kita-ku, Umeda |
4600008 | Nagoya, Naka-ku, Sakae |
Stack
Nuxt 3, deployed to Cloudflare Workers via Wrangler, with GitHub Actions auto-deploying on push to main. The UI is trilingual — 日本語, English, Bahasa Melayu — with a furigana toggle, since a demo about Japanese address input should be readable to the people who would review it.
Full reasoning, including the auto-kana options and why not wanakana, is in docs/COMPARISON.md in the repo.
More projects
view all →microCMS How
Bilingual (EN/JA) news and articles on a flexible content-block system (rich text, image, gallery, profile cards, logo groups), with several contact-form patterns (simple, multi-step, dynamic fields) validated with Zod and vee-validate. Caching is layered — Nitro's server cache plus Vercel's CDN — with a signed microCMS webhook that invalidates the cache on publish/update/delete, so new content shows up fast without spiking API calls or losing the caching benefit.

E-commerce App (MERN)
Electronic commerce app with ReactJS, Context API (useReducer), Styled Components integrate with backend MongoDB database

Mukawa AR Trail
A web AR trail built as a study for the Mukawa dinosaur story. Eight printed boards hang around a building, each with a picture and a QR. The QR only opens a link, so the phone's own camera does the scanning; the app asks for the camera when the visitor taps, recognises the picture itself as an image target, and stands the creature on the board as you move. Every board found unlocks its creature in a collection saved on the phone, with no sign-up. Built end to end by me: the trail app, the image targets, the printed panels and the deploy.