Components
ElvixLanguages

ElvixLanguages

Single-frame wizard for spoken languages and CEFR proficiency, capped at 8 with tap-to-edit and delete-confirm.

app/account/languages/page.tsx
"use client";
import { ElvixLanguages } from "@elvix.is/sdk/react";
export default function LanguagesPane() {
return (
<ElvixLanguages
onChange={(languages) => {
// Optional: mirror to local state for sibling panes. The SDK already persisted.
console.log("languages now:", languages.length);
}}
onResult={(r) => {
if (!r.ok) return console.warn(r.error, r.message);
// r.count is the new total (0 to 8).
}}
/>
);
}

Props

PropTypeRequiredDefaultDescription
heightnumberoptionalmin(540px, 66dvh)Fixed pixel height for the <ElvixCard> frame.
minHeightnumberoptional340Minimum pixel height when height is unset.
maxHeightnumberoptional700Maximum pixel height when height is unset.
widthnumber | stringoptional"100%"Frame width. Numbers are pixels; strings pass through as CSS.
onChange(languages: LanguageRecord[]) => voidoptional—Fires whenever the underlying record set changes (add, edit level, delete). Receives the full list.
onResult(r: ElvixLanguagesResult) => voidoptional—Terminal save callback. Success payload is minimal ({ ok: true, count }); failure is { ok: false, error, message? }.

The picker hides itself the moment the user hits 8 entries. If you see the add affordance disappear in QA, that is the cap doing its job, not a bug. To free a slot, the user taps an existing row and uses the in-frame delete-confirm.

Translated (0.14+)

Everything reads in the <ElvixProvider locale> language: language names come from Intl.DisplayNames ("Deutsch", "allemand", "ドイツ語"), the four level labels and their hints come from the SDK catalog in all fifteen locales, and the picker searches the translated name, the English name, the endonym and the code. Hosts that list a user's languages should use the editor's own words: useElvixLanguageNames() returns each language's name, and useElvixLanguageLevelLabel(level) returns the level ("ELEMENTARY" | "INTERMEDIATE" | "PROFICIENT" | "NATIVE") in the SDK's locale.

onResult.count is intentionally the only success field, so the host never receives the full language list as a side-channel. Subscribe to onChange if you need the records for sibling panes.

Related