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

@jessebyarugaba/ug-address

v1.0.0

Published

Cascading Uganda address selector — District → County → Sub-county → Parish → Village. Single file, no dependencies, works offline.

Readme

ug-address.js

A lightweight, dependency-free JavaScript library for cascading Uganda address selection.

Supports:

District → County/Division → Sub-county → Parish → Village

The library is completely UI-agnostic:

  • no CSS
  • no HTML
  • no framework dependencies
  • no DOM coupling

You can use it with:

  • plain HTML
  • React
  • Vue
  • Svelte
  • Alpine
  • mobile webviews
  • Electron
  • or any custom UI system

Features

  • Single-file library (ug-address.js)
  • No external datasets required
  • No fetch/CORS issues
  • No async initialization
  • Works offline
  • Works with file://
  • Fully event-driven
  • Automatically resets downstream selections
  • Framework agnostic
  • Includes all Uganda administrative levels

Included Data

| Level | Approximate Records | |---|---| | Districts | 137 | | Counties / Divisions | ~305 | | Sub-counties | ~2,100 | | Parishes | ~10,300 | | Villages / Cells | ~71,200 |


Installation

Plain HTML

<script src="ug-address.js"></script>

Quick Start

<!DOCTYPE html>
<html>
<head>
  <title>UgAddress Demo</title>
</head>
<body>

<select id="district"></select>

<script src="ug-address.js"></script>

<script>

const addr = new UgAddress();

console.log(
  addr.getDistricts()
);

</script>

</body>
</html>

Full Cascading Example

<!DOCTYPE html>
<html>
<head>
  <title>UgAddress Demo</title>
</head>
<body>

<select id="district"></select>
<select id="county"></select>
<select id="subcounty"></select>
<select id="parish"></select>
<select id="village"></select>

<script src="ug-address.js"></script>

<script>

const addr = new UgAddress();

const districtEl = document.getElementById('district');
const countyEl = document.getElementById('county');
const subcountyEl = document.getElementById('subcounty');
const parishEl = document.getElementById('parish');
const villageEl = document.getElementById('village');

function populate(select, items, placeholder) {

  select.innerHTML = '';

  const first = document.createElement('option');

  first.value = '';
  first.textContent = placeholder;

  select.appendChild(first);

  items.forEach(function(item) {

    const option = document.createElement('option');

    option.value = item.id;
    option.textContent = item.name;

    select.appendChild(option);

  });

  select.disabled = items.length === 0;

}

populate(
  districtEl,
  addr.getDistricts(),
  'Select District'
);

districtEl.addEventListener('change', function () {

  addr.selectDistrict(this.value);

});

countyEl.addEventListener('change', function () {

  addr.selectCounty(this.value);

});

subcountyEl.addEventListener('change', function () {

  addr.selectSubcounty(this.value);

});

parishEl.addEventListener('change', function () {

  addr.selectParish(this.value);

});

villageEl.addEventListener('change', function () {

  addr.selectVillage(this.value);

});

addr.onDistrictChange(function (e) {

  populate(
    countyEl,
    e.counties,
    'Select County'
  );

});

addr.onCountyChange(function (e) {

  populate(
    subcountyEl,
    e.subcounties,
    'Select Subcounty'
  );

});

addr.onSubcountyChange(function (e) {

  populate(
    parishEl,
    e.parishes,
    'Select Parish'
  );

});

addr.onParishChange(function (e) {

  populate(
    villageEl,
    e.villages,
    'Select Village'
  );

});

</script>

</body>
</html>

API Reference


Constructor

const addr = new UgAddress();

Data Getters

All methods return arrays sorted alphabetically.


getDistricts()

Returns all districts.

addr.getDistricts();

getCounties(districtId?)

Returns counties/divisions for a district.

addr.getCounties('32');

If no argument is provided, the currently selected district is used.


getSubcounties(countyId?)

Returns sub-counties for a county.

addr.getSubcounties('69');

getParishes(subcountyId?)

Returns parishes for a sub-county.

addr.getParishes('1546');

getVillages(parishId?)

Returns villages/cells for a parish.

addr.getVillages('9127');

Selection Methods

Selecting a level automatically clears downstream selections.


selectDistrict(id)

addr.selectDistrict('32');

Fires:

districtChange

selectCounty(id)

addr.selectCounty('69');

Fires:

countyChange

selectSubcounty(id)

addr.selectSubcounty('1546');

Fires:

subcountyChange

selectParish(id)

addr.selectParish('9127');

Fires:

parishChange

selectVillage(id)

addr.selectVillage('57217');

Fires:

villageChange

Event Listeners

The library is event-driven.

Each callback receives pre-filtered child records ready for your next dropdown.


onDistrictChange()

addr.onDistrictChange(function (e) {

  console.log(e.districtId);

  console.log(e.counties);

});

onCountyChange()

addr.onCountyChange(function (e) {

  console.log(e.subcounties);

});

onSubcountyChange()

addr.onSubcountyChange(function (e) {

  console.log(e.parishes);

});

onParishChange()

addr.onParishChange(function (e) {

  console.log(e.villages);

});

onVillageChange()

addr.onVillageChange(function (e) {

  console.log(e.villageId);

});

Reading the Selection

getFormattedAddress()

Returns a formatted human-readable address.

addr.getFormattedAddress();

Example:

Nakasero Village A, Nakasero Parish, Central Ward, Kampala Central Division, Kampala

Reset

reset()

Clears all selected levels.

addr.reset();

Example Data Structure

District
└── County / Division
    └── Sub-county
        └── Parish
            └── Village / Cell

Relationships:

county.district === district.id
subcounty.county === county.id
parish.subcounty === subcounty.id
village.parish === parish.id

Browser Support

Supports all modern browsers:

  • Chrome
  • Firefox
  • Edge
  • Safari
  • Opera

Performance Notes

The library ships with the full Uganda administrative dataset embedded directly into the single JS file.

Advantages:

  • no external requests
  • instant access
  • offline support
  • zero configuration
  • works without a server

License

MIT License

Use freely in:

  • commercial projects
  • SaaS products
  • open-source applications
  • government systems
  • internal tools
  • educational projects