Magento 2 Experten — Hyvä Theme, Tailwind CSS & SEO aus einer Hand ›

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

src/hooks/useTags.ts
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.