Skip to content

Combobox not updating with reactive data in Svelte 5 ($state)Β #3600

Open
@Yeison07

Description

@Yeison07

Current Behavior

Hi, I'm using the Combobox component from @skeletonlabs/skeleton-svelte with Svelte 5 . I'm updating the data prop dynamically based on user input, fetching countries from a local JSON file when the input has 2 or more characters. The countries array updates correctly (confirmed via console logs and rendering in a <select> element), but the Combobox doesn't reflect these changes until an additional input event occurs. For example, when typing "co", it shows 8 countries in logs but the Combobox remains empty; when I delete a character (to "c"), it shows the previous 8 countries instead of clearing. Using a native <select> works perfectly. Is there a known issue with Combobox reactivity in Svelte 5, or am I missing a specific prop/event to make it update dynamically? Here's my code:

<script lang="ts">
	import { Combobox } from '@skeletonlabs/skeleton-svelte';

	interface ComboboxData {
		label: string;
		value: string;
		emoji: string;
	}

	interface CountryResponse {
		translations: { es: string };
		iso3: string;
		emoji?: string;
	}

	const COUNTRIES_DATA: CountryResponse[] = [
		{ translations: { es: 'Argentina' }, iso3: 'ARG', emoji: 'πŸ‡¦πŸ‡·' },
		{ translations: { es: 'Bolivia' }, iso3: 'BOL', emoji: 'πŸ‡§πŸ‡΄' },
		{ translations: { es: 'Colombia' }, iso3: 'COL', emoji: 'πŸ‡¨πŸ‡΄' },
		{ translations: { es: 'Costa Rica' }, iso3: 'CRI', emoji: 'πŸ‡¨πŸ‡·' },
		{ translations: { es: 'Comoras' }, iso3: 'COM', emoji: 'πŸ‡°πŸ‡²' },
		{ translations: { es: 'Congo' }, iso3: 'COG', emoji: 'πŸ‡¨πŸ‡¬' },
		{ translations: { es: 'Chile' }, iso3: 'CHL', emoji: 'πŸ‡¨πŸ‡±' },
		{ translations: { es: 'Ecuador' }, iso3: 'ECU', emoji: 'πŸ‡ͺπŸ‡¨' },
		{ translations: { es: 'MΓ©xico' }, iso3: 'MEX', emoji: 'πŸ‡²πŸ‡½' },
		{ translations: { es: 'PerΓΊ' }, iso3: 'PER', emoji: 'πŸ‡΅πŸ‡ͺ' }
	];

	let countries: ComboboxData[] = $state([]);
	let selectedCountry = $state(['']);

	async function searchCountries(input: string): Promise<void> {
		try {
			console.log(input.length, 'size');
			if (input.length >= 2) {
				console.log('search');
				const filtered = COUNTRIES_DATA.filter(
					(country) =>
						country.translations?.es &&
						country.translations.es.toLowerCase().startsWith(input.toLowerCase())
				);
				console.log('FILTERED:', filtered);
				countries = [
					...filtered.map(({ translations, iso3, emoji }) => ({
						label: translations.es ?? '',
						value: iso3 ?? '',
						emoji: emoji || ''
					}))
				];
				console.log('Countries updated:', countries);
			} else {
				console.log('clear');
				countries = [];
			}
		} catch (e) {
			console.error('Error fetching countries:', e);
			countries = [];
		}
	}
</script>

<div style="margin-bottom: 24px;">
	<h2
		style="border-bottom: 1px solid #ccc; padding-bottom: 8px; font-size: 1.25rem; font-weight: bold; color: #333; display: flex; align-items: center; gap: 8px;"
	>
		Location
	</h2>

	<div style="display: grid; grid-template-columns: 1fr; gap: 24px; max-width: 800px;">
		<div style="margin-bottom: 16px;">
			<label
				style="display: flex; align-items: center; gap: 8px; font-size: 0.95rem; font-weight: 500; color: #444;"
				for="country"
			>
				Country
			</label>
			<Combobox
				data={countries}
				value={selectedCountry}
				onValueChange={(e) => (selectedCountry = e.value)}
				onInputValueChange={(e) => searchCountries(e.inputValue)}
				label=""
				placeholder="Select..."
			>
				{#snippet item(item)}
					<div style="display: flex; justify-content: space-between; width: 100%; gap: 8px;">
						<span>{item.label}</span>
						<span>{item.emoji}</span>
					</div>
				{/snippet}
			</Combobox>

			<select bind:value={selectedCountry} style="width: 100%; padding: 6px; margin-top: 8px;">
				{#each countries as country}
					<option value={country.value}>{country.label} {country.emoji}</option>
				{/each}
			</select>
		</div>
	</div>
</div>

Expected Behavior

  • The Combobox should dynamically update its dropdown to reflect changes in the data prop immediately.
  • Typing 2+ characters (e.g., "co") should display filtered countries (e.g., "Colombia", "Costa Rica").
  • Deleting to <2 characters (e.g., "c") should clear the dropdown.
  • The behavior should match a native <select>, which renders the same data correctly.

Steps To Reproduce

  1. Set up a Svelte 5 project with @skeletonlabs/skeleton-svelte.
  2. Copy and paste the code attached.
  3. Type "co" in the Combobox input.
  4. Observe that the Combobox does not display the filtered countries, despite the data updating correctly (verified via console logs and a native <select>).
  5. Delete to "c" and observe that the Combobox shows the previously filtered countries instead of an empty dropdown.

Link to Reproduction / Stackblitz

No response

Environment Information

System:
OS: Linux 6.11 Linux Mint 22.1 (Xia)
CPU: (16) x64 AMD Ryzen 7 7730U with Radeon Graphics
Memory: 8.13 GB / 15.03 GB
Container: Yes
Shell: 5.2.21 - /bin/bash
Binaries:
Node: 22.13.0 - ~/.volta/tools/image/node/22.13.0/bin/node
npm: 10.9.2 - ~/.volta/tools/image/node/22.13.0/bin/npm
pnpm: 9.15.4 - ~/.volta/bin/pnpm
Browsers:
Brave Browser: 138.1.80.115
Chrome: 138.0.7204.92
npmPackages:
@skeletonlabs/skeleton: ^3.1.4 => 3.1.4
@skeletonlabs/skeleton-svelte: ^1.2.4 => 1.2.4

More Information

I noticed that when I type β€œCo” and the comboBox doesn't show anything if I remove the focus and put it back on the input it loads correctly, seems like a synchronization problem?

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions