Flag icons from 5 upstream sources — Twemoji, Circle (HatScripts), Square (kapowaz), Lipis (lipis/flag-icons), FlagHub (Alpaq92 — maintained FlagKit fork) — packaged as drop-in controls for Avalonia, Eto.Forms, .NET MAUI, Aprillz.MewUI, Uno Platform, Windows Forms, WinUI 3 and WPF. Every SVG ships as an embedded resource in the core Flags.Icons package, reachable through a per-source strongly-typed enum (TwemojiFlag, CircleFlag, SquareFlag, LipisFlag, FlagHubFlag). No runtime download, no file-system access. Platform packages are thin wrappers that convert the embedded streams into the native image type for each UI stack.
Version notes
- v3 brings back the FlagKit-style artwork that originally shipped in v1 — now sourced from Alpaq92/FlagHub, a maintained fork of the abandoned madebybowtie/FlagKit with open upstream PRs merged and packaging fixed. It joins the v2 line-up as a fifth source via the new
FlagHubFlagenum /FlagHub="US"DP. Additive, non-breaking on top of v2 — existingTwemoji/Circle/Square/Lipiscode keeps working unchanged. Still breaking vs v1: there is noFlagKindenum anywhere — useFlagHub="US"(or any of the other four sources) instead of the oldKind="USSVG".- v2 dropped the unmaintained
FlagKitsubmodule (no flag refreshes since 2019, no newer subdivision codes) and replaced the singleFlagKindenum with one typed enum per source. Migration from v1: swap<flag:FlagIcon Kind="USSVG"/>for<flag:FlagIcon Twemoji="US"/>(orCircle="US"/Square="US"/Lipis="US"/ nowFlagHub="US"— enum members are uppercase ISO codes regardless of how the upstream stores filenames). API surface details in the Usage section.
Counts as of 2026-06-01 (latest monthly submodule bump — see .github/workflows/monthly-submodule-bump.yml). Actual numbers in any given build are whatever the upstream submodule pins currently contain; check the staging log line <Source> → assets/<dir>: N SVGs ... printed by dotnet build for the live count.
| Source | Style | Count | Member name shape (ISO uppercase) |
|---|---|---|---|
| jdecked/twemoji | Emoji-style flat | 262 | TwemojiFlag.US, TwemojiFlag.GB_ENG |
| HatScripts/circle-flags | Circular | 430 | CircleFlag.US, CircleFlag.AF_EMIRATE |
| kapowaz/square-flags | Square | 417 | SquareFlag.US |
| lipis/flag-icons | Rectangular (4×3) | 271 | LipisFlag.US |
| Alpaq92/FlagHub (FlagKit fork) | Rectangular flat | 259 | FlagHubFlag.US |
All 5 sources live as git submodules under submodules/. The build pipeline (run on every dotnet build) syncs submodules → extracts SVGs into assets/{source}/ (regenerated each build, gitignored) → embeds them as manifest resources → code-generates 5 strongly-typed enums.
| Package | Description | Target framework(s) |
|---|---|---|
Flags.Icons |
Core: 4 generated enums, FlagAssetLoader, FlagIcons index |
netstandard2.0 |
Flags.Icons.Avalonia |
Avalonia FlagIcon templated control |
net8.0 |
Flags.Icons.Eto |
Eto.Forms FlagIcon extending ImageView |
netstandard2.0 |
Flags.Icons.MAUI |
.NET MAUI FlagIcon view |
net10.0 + per-platform TFMs |
Flags.Icons.MewUI |
Aprillz.MewUI fluent helpers (FlagIcon.Create / .Flag()) |
net10.0 |
Flags.Icons.Uno |
Uno Platform FlagIcon control |
net10.0 + per-platform TFMs |
Flags.Icons.WinForms |
Windows Forms FlagIcon extending PictureBox |
net8.0-windows |
Flags.Icons.WinUi |
WinUI 3 FlagIcon control |
net8.0-windows10.0.19041.0 |
Flags.Icons.WPF |
WPF FlagIcon control |
net8.0-windows |
Every bundled flag is SVG. Stacks without a native SVG type (Eto, MAUI, MewUI, WinForms) rasterize at runtime through Svg.Skia. Avalonia uses Svg.Controls.Skia.Avalonia, WPF uses SharpVectors, Uno/WinUI use the framework's SvgImageSource.
dotnet add package Flags.Icons.Avalonia # or .Eto / .MAUI / .MewUI / .Uno / .WinForms / .WinUi / .WPFEvery platform package transitively pulls in Flags.Icons core, so you don't need to reference it separately.
FlagIcon exposes one property per source — Twemoji, Circle, Square, Lipis, FlagHub. Set exactly one; the others are cleared automatically.
In App.axaml:
<StyleInclude Source="avares://Flags.Icons.Avalonia/App.xaml" />Then:
<Window xmlns:flag="clr-namespace:Flags.Icons.Avalonia;assembly=Flags.Icons.Avalonia">
<flag:FlagIcon Twemoji="US" Width="48" Height="36" />
<flag:FlagIcon Circle="US" Width="48" Height="36" />
<flag:FlagIcon FlagHub="US" Width="48" Height="36" />
<Button Content="{flag:FlagIconExt Lipis=FR, Size=24}" />
</Window>using Flags.Icons;
using Flags.Icons.Eto;
var flag = new FlagIcon { Twemoji = TwemojiFlag.US, Size = new Size(48, 36) };<ContentPage xmlns:flag="clr-namespace:Flags.Icons.Maui;assembly=Flags.Icons.MAUI">
<flag:FlagIcon Square="US" WidthRequest="48" HeightRequest="36" />
</ContentPage>using Flags.Icons;
using Flags.Icons.MewUi;
var flag = FlagIcon.Create(TwemojiFlag.US, 48, 36);
var img = new Image().Flag(CircleFlag.FR).Width(24).Height(18);<Page xmlns:flag="using:Flags.Icons.Uno">
<flag:FlagIcon Lipis="US" Width="48" Height="36" />
</Page>using Flags.Icons;
using Flags.Icons.WinForms;
var flag = new FlagIcon { Twemoji = TwemojiFlag.US, Width = 48, Height = 36 };<Window xmlns:flag="using:Flags.Icons.WinUi">
<flag:FlagIcon Circle="US" Width="48" Height="36" />
</Window><Window xmlns:flag="clr-namespace:Flags.Icons.WPF;assembly=Flags.Icons.WPF">
<flag:FlagIcon Twemoji="US" Width="48" Height="36" />
</Window>using Flags.Icons;
// Enumerate per source
foreach (var f in FlagIcons.TwemojiFlags) { /* TwemojiFlag values, e.g. US, FR, GB_ENG, ... */ }
foreach (var f in FlagIcons.CircleFlags) { /* CircleFlag values */ }
foreach (var f in FlagIcons.SquareFlags) { /* SquareFlag values */ }
foreach (var f in FlagIcons.LipisFlags) { /* LipisFlag values */ }
foreach (var f in FlagIcons.FlagHubFlags) { /* FlagHubFlag values */ }
// Open the raw embedded SVG
using Stream? raw = FlagAssetLoader.OpenStream(TwemojiFlag.US);
// Original on-disk filename (preserves upstream casing)
string? fileName = TwemojiFlagFiles.GetFileName(TwemojiFlag.US); // "US.svg"
string? fileName2 = CircleFlagFiles.GetFileName(CircleFlag.US); // "us.svg"
string? fileName3 = FlagHubFlagFiles.GetFileName(FlagHubFlag.US); // "US.svg"Each UI stack ships a demo that renders every flag in a wrapping grid with country-code search, source picker (Twemoji / Circle / Square / Lipis / FlagHub / All), section headers, and click-to-copy markup snippets.
dotnet run --project Flags.Icons.Avalonia.Demo
dotnet run --project Flags.Icons.Eto.Demo # cross-platform
dotnet run --project Flags.Icons.MAUI.Demo # Windows or macCatalyst
dotnet run --project Flags.Icons.MewUI.Demo # cross-platform
dotnet run --project Flags.Icons.Uno.Demo # desktop
dotnet run --project Flags.Icons.WinForms.Demo # Windows
dotnet run --project Flags.Icons.WinUi.Demo # Windows (x64/ARM64)
dotnet run --project Flags.Icons.WPF.Demo # WindowsAll 5 upstream sources are git submodules under submodules/. Clone with submodules:
git clone --recurse-submodules https://github.com/Alpaq92/Flags.Icons.git…or after a plain clone (the first dotnet build will also auto-init if it sees submodules/ empty):
git submodule update --init --recursive| Path | Upstream | Build picks |
|---|---|---|
submodules/twemoji/ |
jdecked/twemoji | assets/svg/*.svg, filtered to country (1f1XX-1f1YY) + subdivision (1f3f4-…-e007f) emoji, renamed to ISO codes |
submodules/circle-flags/ |
HatScripts/circle-flags | flags/*.svg (full set; ~24 Windows-clone symlink stubs auto-resolved) |
submodules/square-flags/ |
kapowaz/square-flags | flags/*.svg (curated subset, not flags-original/) |
submodules/flag-icons/ |
lipis/flag-icons | flags/4x3/*.svg |
submodules/flaghub/ |
Alpaq92/FlagHub | Assets/SVG/*.svg (maintained fork of madebybowtie/FlagKit) |
build/Flags.Icons.Assets.targets runs on every dotnet build and is data-driven: a single <_FlagSource> MSBuild ItemGroup describes every upstream (mode, submodule path, staging dir, logical-name prefix, enum name, human label) and every downstream target batches over it. Adding a sixth source = one new row in that ItemGroup plus a matching <submodule> entry in .gitmodules — nothing else in the targets file changes.
-
Sync submodules —
_AutoSyncSubmodulesOnFirstBuildchecks each_FlagSource'sUpstreamPathand runsgit submodule update --init --recursiveif any are missing. Skipped on CI (actions/checkout submodules:truealready did it). Manual force-sync:dotnet msbuild Flags.Icons/Flags.Icons.csproj -t:SyncSubmodules. -
Stage assets —
_StageFlagAssetstransforms_FlagSourceinto one_FlagStagingJobper source and hands the batch to a singleStageFlagAssetstask that processes them in parallel (Parallel.ForEach). Two modes:Mode="TwemojiFilter"for twemoji — regex-filters for country + subdivision codepoints and renames to ISO codes.Mode="RawCopy"for the other 4 — copies every*.svgverbatim, resolving Windows-clone symlink stubs along the way.
Both modes share write-if-different + prune-stale logic in the same task, so subsequent builds keep stable mtimes (incremental-friendly) and upstream-removed files disappear from
assets/.assets/is gitignored — pure build output. -
Collect & embed —
_CollectFlagAssetsuses a two-step cross-batch: step 1 globs each source's staging dir (Include="%(_FlagSource.StagingDir)\*.svg") and stamps per-source metadata on each match; step 2 then references each item's own%(LogicalPrefix)+ well-known%(Filename)%(Extension)to build the LogicalName (the split avoids the%(Filename)cross-batch ambiguity where it would otherwise bind to the_FlagSourceidentity). Each staged SVG becomes anEmbeddedResourceatassets/{source}/{filename}.svg. -
Generate enums —
_GenerateFlagEnumsinvokesGenerateFlagEnumonce per_FlagSource, producing{EnumName}.g.csinobj/(e.g.TwemojiFlag.g.cs) with the enum + a{EnumName}Files.GetFileName(flag)lookup that preserves upstream filename casing. The task receives the full staged-SVG set plus the currentSourceIdand filters internally — simpler than fighting MSBuild'sWithMetadataValue(..., %(batch))cross-batch resolution._GenerateFlagEnumsshort-circuits viaInputs/Outputswhen the staged set is unchanged;_IncludeGeneratedFlagEnumsis a safety net that re-adds any existing.g.csto Compile on those skipped second-build runs.
Runtime helpers in Flags.Icons (the core package): FlagAssetLoader.OpenStream has 5 typed overloads (one per source enum); FlagSourceDispatch.OpenActive/GetActive is the shared dispatch the 8 per-stack FlagIcon controls use to walk their 5 source properties (DependencyProperty / BindableProperty / StyledProperty / plain CLR property depending on the stack) and pick the active one.
If a submodule is missing (e.g. CI checked out without submodules), the corresponding enum will only contain None = 0 and a warning is emitted.
MIT for source code. Bundled flag SVGs come from:
- jdecked/twemoji — graphics CC-BY 4.0 (attribution: Twemoji © Twitter, Inc / jdecked contributors), code MIT.
- HatScripts/circle-flags — MIT.
- kapowaz/square-flags — MIT.
- lipis/flag-icons — MIT.
- Alpaq92/FlagHub — MIT (maintained fork of madebybowtie/FlagKit, MIT; artwork identical to upstream).
The project icon (icon.png) is from the Jellyfin UX icon set, licensed under CC BY-SA 4.0.
- Flag assets — jdecked/twemoji, HatScripts/circle-flags, kapowaz/square-flags, lipis/flag-icons, Alpaq92/FlagHub (FlagKit fork)
- Icon — Jellyfin UX (CC BY-SA 4.0)
- Inspiration — Material.Icons.Avalonia — templated-control + markup-extension pattern adapted from this project
Parts of this codebase — the per-platform FlagIcon wrappers, demo scaffolding, and CI/CD configuration — were developed with assistance from an audit-based AI workflow, with each change reviewed and verified by a human maintainer before being merged. Bug reports and PRs are welcome.
Release history in CHANGELOG.md. Versions are driven by Conventional Commits via release-please; branch ruleset details in .github/rulesets/README.md.