Selecting Tags: a Multi-Select
Selecting Tags: a Multi-Select
~14 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
Tags (chapter 12) are READ-ONLY via the API – the frontend only READS them, to DISPLAY them in a selection element, EXACTLY as described in chapter 42.
Loading all tags
import { useQuery } from '@tanstack/react-query';
import { apiClient } from '../api/client';
interface Tag {
'@id': string;
id: number;
name: string;
}
interface TagCollection {
'hydra:member': Tag[];
}
export function useTags() {
return useQuery({
queryKey: ['tags'],
queryFn: async () => {
const response = await apiClient.get<TagCollection>('/tags');
return response.data['hydra:member'];
},
staleTime: Infinity,
});
}staleTime: Infinity instead of the 30 seconds from chapter 73 – tags change (chapter 12: ONLY via fixtures/admin) SO RARELY that a repeated automatic refetch would be WASTEFUL.
The multi-select element
function TagSelector({
selectedIris,
onChange,
}: {
selectedIris: string[];
onChange: (iris: string[]) => void;
}) {
const { data: tags, isPending } = useTags();
if (isPending) {
return null;
}
function toggle(iri: string) {
if (selectedIris.includes(iri)) {
onChange(selectedIris.filter((current) => current !== iri));
} else {
onChange([...selectedIris, iri]);
}
}
return (
<div>
{tags.map((tag) => (
<label key={tag.id}>
<input
type="checkbox"
checked={selectedIris.includes(tag['@id'])}
onChange={() => toggle(tag['@id'])}
/>
{tag.name}
</label>
))}
</div>
);
}selectedIris: string[] stores the SELECTED tags DIRECTLY as an IRI array – EXACTLY the format that useCreateTask (chapter 87) EXTENDED with a tags field could send DIRECTLY to the API, WITHOUT any further conversion.
Achtung: TagSelector is a "controlled component" (the CALLING parent holds the state via selectedIris/onChange) – this pattern allows the SAME selector to be reused BOTH in the create AND the edit form, WITHOUT its own internal state logic.
Integrating into the task form
const [selectedTagIris, setSelectedTagIris] = useState<string[]>([]);
// in the JSX:
<TagSelector selectedIris={selectedTagIris} onChange={setSelectedTagIris} />
// on submit:
createTask.mutate({ title, projectId, tags: selectedTagIris });Tipp: A REAL dropdown/combobox (instead of checkboxes) would be MORE USER-FRIENDLY for MANY tags – libraries like react-select solve THIS problem out of the box, the BASIC principle (an IRI array as state) stays the SAME INDEPENDENT of the chosen UI library.