Adding New Icons
A practical checklist for what to actually do — and what you can safely skip — when a new icon is added to any mapping.json.
Short answer: nothing breaks if you forget a step below. Worst case, a new icon just isn’t French-searchable yet — it still works exactly like every icon did before this feature existed.
What’s required vs. optional
| Step | Required? | What happens if you skip it |
|---|---|---|
Add the icon to its mapping.json | Yes (obviously) | — |
Re-run python script/generate-keywords.py | Yes | The new icon has no keywords at all — still findable by its English name, just not by French/synonyms |
Add words to translations-en-fr.json | No | The icon keeps whatever fallback keywords the script can build (see below) — just not French for the untranslated words |
Add an entry to keyword-overrides.json | No | Only relevant if it’s a brand/product name needing a non-literal synonym (see Keywords & Translations) |
Change anything in IconDropdown.tsx | Only for a brand-new icon set folder outside phpc/ — see below | — |
Full worked examples, A to Z
The table above tells you what’s required. These three walk through doing it for real, one per library — every command, every file touched, nothing skipped:
- Example: Adding an Icon to
glyphicons-v2 - Example: Adding an Icon to
glyphicons-v1 - Example: Adding an Icon to
phpc
Nothing regenerates automatically. Adding an icon to mapping.json and
forgetting to re-run the script leaves the generated files stale — same
rule as the pre-existing generate-mapping.py /
generate-master-mapping.py scripts in this project.
Adding a whole new icon set
For a brand-new sub-folder (e.g. adding filetypes to glyphicons-v1) or a whole new top-level library — the folder and its mapping.json don’t exist yet.
Verified against src/components/IconDropdown.tsx,
script/generate-mapping.py and script/generate-keywords.py in
frontend-icons-react-nextjs-bundle
(main branch).
How much work depends on two things
IconDropdown.tsx: one import per sub-folder, or one combined file?
glyphicons-v1 imports each sub-folder’s mapping.json by name — one import per sub-folder, forever:
import iconsGlyphiconsV1Basic from "../../icons/glyphicons-v1/basic/mapping.json";
import iconsGlyphiconsV1Filetypes from "../../icons/glyphicons-v1/filetypes/mapping.json";glyphicons-v2 and phpc each import one file instead, rebuilt by script/generate-mapping.py (step 3 below):
import iconsGlyphiconsV2 from "../../icons/glyphicons-v2/mapping-with-filepath.json";
import iconsPHPC from "../../icons/phpc/mapping-with-filepath.json";Real constraint, not a style choice: a JS import path has to be written by a person ahead of time — a script that walks the filesystem doesn’t.
generate-keywords.py: hand-written list, or auto-discovered?
Both scripts build a sets list of (mapping.json, ..., keywords.json, ...) entries. glyphicons-v2’s are hand-written, one tuple per sub-folder:
("icons/glyphicons-v2/basic/mapping.json", "", "icons/glyphicons-v2/basic/keywords.json", "-"),
("icons/glyphicons-v2/halflings/mapping.json", "", "icons/glyphicons-v2/halflings/keywords.json", "-"),phpc’s are discovered instead, with no folder name written anywhere:
for mapping_path in sorted(glob.glob("icons/phpc/**/mapping.json", recursive=True)):
out_path = mapping_path.replace("mapping.json", "keywords.json")
sets.append((mapping_path, "", out_path, "-"))Not a hard limit — glob.glob(...) could replace glyphicons-v2’s 3
tuples too. Nobody has, since 3 lines wasn’t worth automating the way 46
was for phpc. Technical debt, not a constraint.
1. Create the folder
icons/<library>/<subfolder>/, containing the .svg files plus one mapping.json:
icons/
├─ glyphicons-v1/{basic,halflings,filetypes,socials}/
├─ glyphicons-v2/{basic,halflings,filetypes}/ ← e.g. a new subfolder here
└─ phpc/2. Normalize mapping.json
| Meaning | |
|---|---|
| Key | The icon’s classname, as shown to the user |
| Value | The real icon file name, with extension |
{
"file-bookmark": "glyphicons-filetypes-89-file-bookmark.svg",
"file-stats": "glyphicons-filetypes-31-file-stats.svg"
}glyphicons-v1 bakes the sub-library name into the key twice — e.g.
"filetypes filetypes-txt": "icon_uniE001.svg". Every other library uses
the plain key above.
If your source data isn’t already in that shape
Raw exports sometimes reverse the key/value roles, or use ligature codes instead of filenames. jq reshapes it fast.
Example — value is a ligature code, not a filename:
{
"glyphicons-txt": "E001",
"glyphicons-doc": "E002"
}Turn each value into a real file name
The SVGs are named icon_uni<CODE>.svg, so prepend/append accordingly:
jq 'map_values("icon_uni" + . + ".svg")' mapping.json > new_mapping.jsonStrip the redundant prefix from each key
jq 'with_entries(.key |= sub("glyphicons-"; ""))' new_mapping.json > mapping.jsonResult:
{
"txt": "icon_uniE001.svg",
"doc": "icon_uniE002.svg"
}map_values(...) rewrites every value; with_entries(.key |= sub(...)) rewrites every key. Adapt both filters to your source export.
3. Get the icons into the dropdown
A. glyphicons-v2 or phpc — a new sub-folder under an existing library
Nothing to import by hand — both rebuild from one combined file:
cd icons/
python ../script/generate-mapping.py ./glyphicons-v2
# writes mapping-with-filepath.json — move it into glyphicons-v2/ if it isn't already thereIconDropdown.tsx already imports that file — a new sub-folder is picked up the next time this script runs, no code change:
import iconsGlyphiconsV2 from "../../icons/glyphicons-v2/mapping-with-filepath.json";Data only. glyphicons-v2 keywords aren’t auto-discovered — see the
keywords question
above: add a
tuple to sets in script/generate-keywords.py, and wire the new
keywords.json into v2KeywordSets in IconDropdown.tsx (see Search in
IconDropdown). phpc is
the only library where both data and keywords are fully automatic.
B. glyphicons-v1, font-awesome, or a genuinely new top-level library
Hardcoded in IconDropdown.tsx: every sub-folder gets its own import plus a manual lookup entry.
Import the new mapping.json (and its keywords.json)
import iconsGlyphiconsV1Filetypes from "../../icons/glyphicons-v1/filetypes/mapping.json";
import iconsGlyphiconsV1FiletypesKeywords from "../../icons/glyphicons-v1/filetypes/keywords.json";Add it to the library’s lookup objects
One <library>Sets object (the imported mapping) and one matching <library>KeywordSets object, both keyed by "library/subfolder":
const v1Sets = {
"glyphicons-v1/basic": iconsGlyphiconsV1Basic,
"glyphicons-v1/filetypes": iconsGlyphiconsV1Filetypes, // new
// ...other existing entries
};
const v1KeywordSets: Record<string, Record<string, string[]>> = {
"glyphicons-v1/basic": iconsGlyphiconsV1BasicKeywords,
"glyphicons-v1/filetypes": iconsGlyphiconsV1FiletypesKeywords, // new
// ...other existing entries
};Add both entries together — the loop right after reads them by the same prefix key, building each IconOption in one pass:
Object.entries(v1Sets).forEach(([prefix, iconSet]) => {
const keywordSet = v1KeywordSets[prefix];
Object.entries(iconSet).forEach(([name, path]) => {
iconsArray.push({
value: `${prefix}/${name}`,
label: name,
iconUrl: `${baseUrl}${prefix}/${path}`,
keywords: keywordSet?.[name],
});
});
});The access flag is per top-level library, not per sub-folder —
canAccessGlyphiconV1 already covers glyphicons-v1/filetypes. A new
flag is only needed for a genuinely new top-level library.
Related pages
- Search in IconDropdown — how
matchesSearchand thekeywordsfield actually work - Keywords & Translations — the source files behind that keyword search, and how they’re combined