Skip to content

State → LGA → ward dropdowns

The most common use of naija-data: a form where people pick their state, then an LGA in that state, then (optionally) their ward. Every list is official (INEC’s current names and codes), so you never have to copy an old list from the internet again.

  1. Fill the State dropdown with the 37 states (36 states + FCT).
  2. When someone picks a state, fill the LGA dropdown with that state’s LGAs only.
  3. When someone picks an LGA, fill the Ward dropdown with its wards.
  4. The form submits codes, not names: state LA, LGA NG025011, ward 24-11-01.
  5. Your server checks the codes again before saving (the browser can be bypassed).

No install and no build step: the browser asks the free REST API for each list. Best for simple sites, WordPress, Webflow, or any page where you can add a <script>.

<label for="state">State</label>
<select id="state" name="state" required></select>
<label for="lga">LGA</label>
<select id="lga" name="lga" required disabled></select>
<label for="ward">Ward</label>
<select id="ward" name="ward" disabled></select>
<script>
const API = "https://naija-data-api.asgatcreation.workers.dev/v1";
const stateSelect = document.getElementById("state");
const lgaSelect = document.getElementById("lga");
const wardSelect = document.getElementById("ward");
// value = code, text = name
function fill(select, records, placeholder) {
select.replaceChildren(new Option(placeholder, ""), ...records.map((r) => new Option(r.name, r.code)));
select.disabled = records.length === 0;
}
async function getList(path) {
const res = await fetch(API + path);
return (await res.json()).data; // { count, data: [...] }
}
getList("/states").then((states) => fill(stateSelect, states, "Choose a state"));
stateSelect.addEventListener("change", async () => {
fill(wardSelect, [], "Choose an LGA first");
fill(lgaSelect, stateSelect.value ? await getList(`/states/${stateSelect.value}/lgas`) : [], "Choose an LGA");
});
lgaSelect.addEventListener("change", async () => {
fill(wardSelect, lgaSelect.value ? await getList(`/lgas/${lgaSelect.value}/wards`) : [], "Choose a ward");
});
</script>

The API allows about 120 requests a minute per visitor and responses are cached by the browser, so normal forms never hit the limit. For very busy sites, use the CDN or npm version instead.

Anyone can send your server any value, so check that the LGA really is in the chosen state before saving.

import { getState, getLga } from "naija-data";
function checkAddress(stateCode: string, lgaCode: string): string | null {
const state = getState(stateCode);
if (!state) return "Choose a valid state.";
const lga = getLga(lgaCode);
if (!lga || lga.state_code !== state.code) return "Choose an LGA in the selected state.";
return null; // valid
}
checkAddress("LA", "NG025011"); // null (Ikeja is in Lagos)
checkAddress("KN", "NG025011"); // "Choose an LGA in the selected state."
  • FCT. The Federal Capital Territory has 6 Area Councils instead of LGAs; they come back from the same functions, so your form needs no special case. People may type “Abuja” or “FCT”: getState("Abuja") finds it.
  • Same LGA name in two states. “Surulere” exists in Lagos and Oyo, “Obi” in Benue and Nasarawa. Because the LGA list depends on the chosen state, and you store the LGA code, there is never any confusion.
  • Wards are INEC registration areas. Most forms only need state and LGA; add wards for voter, census, health or field-work data.
  • Show more in the dropdown. Every LGA record also has headquarters (where known), senatorial_district and aliases (older spellings people may search for). For a typing box instead of a dropdown, see search and autocomplete.
  • Credit. The data is free under CC BY 4.0: add “Data: naija-data by Akanji Oluwaseun Gabriel” to your site’s credits or about page. See using the data.