Shared language switcher component for PhoenixKit.
A configurable row of language options that works across all contexts: admin form tabs, publishing editors, public page navigation, etc.
Display modes
:auto(default) — shows full names when ≤auto_thresholdlanguages, short codes when more:full— always show full names (e.g., "English", "French"):compact— always show short codes (e.g., "EN", "FR")
Visual variants
:inline(default) — pipe-separated items:EN | FR | ES:tabs— pill-shaped buttons on a rounded background bar:pills— individual pill-shaped chips, each with its own background
Interaction modes
- Button — set
on_click(event name) oron_click_js(fn returning%JS{}) - Link — each language map has a
:urlkey, renders<.link navigate={url}> - Display-only — no click handler and no URL, renders a
<span>
Status dots
When show_status is true, a colored dot appears before each language code.
The dot color is resolved in priority order:
dot_color— explicit daisyUI color class (e.g.,"success","warning")status— mapped automatically:"published"→ green,"draft"→ yellow,"archived"→ grayexists—true→ green dot,false→ dim dot
This makes the dots work for any use case: publishing post status, form content indicators ("has translations"), or custom per-module states.
Examples
<%!-- Admin form: tabs with primary star, skeleton switching --%>
<.language_switcher
languages={@language_tabs}
current_language={@current_lang}
on_click_js={&switch_lang_js(&1, @current_lang)}
show_primary={true}
primary_divider={true}
variant={:tabs}
/>
<%!-- Publishing editor: compact codes with status dots --%>
<.language_switcher
languages={@editor_languages}
current_language={@current_language}
on_click_js={&switch_lang_js(&1, @current_language)}
show_status={true}
show_add={true}
primary_divider={true}
/>
<%!-- Public page: navigation links, no dots --%>
<.language_switcher
languages={@translations}
current_language={@current_language}
/>
<%!-- Post overview: pills with status dots and "Primary" label --%>
<.language_switcher
languages={@post_languages}
current_language={@current_language}
show_status={true}
show_primary_label={true}
variant={:pills}
prefix_urls={true}
/>
<%!-- Form with content indicators --%>
<.language_switcher
languages={Enum.map(@language_tabs, &Map.put(&1, :exists, has_content?(&1.code)))}
current_language={@current_lang}
on_click_js={&switch_lang_js(&1, @current_lang)}
show_status={true}
variant={:tabs}
/>
Summary
Functions
Renders a language switcher.
Functions
Renders a language switcher.
Language map structure
Each item in :languages should be a map with:
code(required) — language code (e.g., "en-US", "fr")name— full display name (e.g., "English"). Falls back to uppercased code.short_code— short display code (e.g., "EN"). Auto-derived from code if absent.flag— flag emoji (e.g., "🇺🇸")url— navigation URL (enables link mode)is_primary— boolean, marks the primary languagestatus— "published", "draft", "archived", or nil (for dot color)exists— boolean, whether content exists (inferred from status if absent)dot_color— explicit daisyUI color class override. Valid values:"success","warning","error","info","primary","secondary","accent","neutral","base-content/20","base-content/40". Invalid values are silently ignored (falls back to status/exists color).enabled— boolean, whether this language is enabled in the system (default: true)known— boolean, whether this language code is recognized (default: true)uuid— optional ID, forwarded asphx-value-uuidin button mode
Attributes
languages— list of language maps (required)current_language— currently active language codedisplay—:auto,:full, or:compact. Default::autoauto_threshold— show full names when language count ≤ this. Default: 3show_status— show status indicator dots. Default: falseshow_flags— show flag emojis. Default: falseshow_primary— show star icon on primary language. Default: falseshow_primary_label— show "Primary" text label on primary language. Default: falseshow_add— style missing languages as addable (green). Default: falseexclude_primary— exclude the primary language from the list. Default: falseprimary_divider— show a vertical divider after the primary language. Default: falseon_click— event name for button click. Default: nilon_click_js—fn(lang_code) -> %JS{}for custom click. Default: nilphx_target— target for phx-click. Default: nilvariant—:inline,:tabs, or:pills. Default::inlinesize—:xs,:sm, or:md. Default::smprefix_urls— when true, URLs in language maps are passed throughPhoenixKit.Utils.Routes.path/1for prefix-aware routing. Default: falseid— optional HTML id for the container element. Default: nilclass— additional CSS classes. Default: ""
Notes
auto_thresholdis evaluated against the displayed language count (after filtering andexclude_primary), so excluding the primary language may change the display mode.on_click_jsmust be a pure function returning%Phoenix.LiveView.JS{}— it is called during render.
Attributes
languages(:list) (required)current_language(:string) - Defaults tonil.display(:atom) - Defaults to:auto. Must be one of:auto,:full, or:compact.auto_threshold(:integer) - Defaults to3.show_status(:boolean) - Defaults tofalse.show_flags(:boolean) - Defaults tofalse.show_primary(:boolean) - Defaults tofalse.show_primary_label(:boolean) - Defaults tofalse.show_add(:boolean) - Defaults tofalse.exclude_primary(:boolean) - Defaults tofalse.primary_divider(:boolean) - Defaults tofalse.on_click(:string) - Defaults tonil.on_click_js(:any) - Defaults tonil.phx_target(:any) - Defaults tonil.variant(:atom) - Defaults to:inline. Must be one of:inline,:tabs, or:pills.size(:atom) - Defaults to:sm. Must be one of:xs,:sm, or:md.prefix_urls(:boolean) - Defaults tofalse.id(:string) - Defaults tonil.class(:string) - Defaults to"".