Guide Adresser

# Adressefelt med autocomplete i React

Grundfasts [adressefelt](https://grundfast.dk/docs/adressefelt) er et almindeligt script, der hægter sig på et inputfelt. I React vil du styre, hvornår det sker: når komponenten monteres, og ikke længere når den forsvinder. Her er en hook, der gør netop det, og en komponent, der bruger den.

Kaldet i guiden

- [GET /v1/adresse/search](https://grundfast.dk/docs/api/adresse-search)

3 min læsning 6 trin Opdateret 5. oktober 2026

## Opret en offentlig nøgle

Feltet kalder Grundfast direkte fra kundens browser, så det bruger en **offentlig nøgle** (`gf_pub_live_`). Den må gerne stå i sidens kildekode, fordi den kun virker fra de domæner, du skriver på den, og kun til adresseopslag. Opret den under [Nøgler i dashboardet](https://grundfast.dk/dashboard/keys): vælg **Offentlig** og skriv dine domæner, fx `minapp.dk` og `localhost:5173`. Den lokale port er den, din udviklingsserver kører på.

Byg og test med en offentlig testnøgle (`gf_pub_test_`), som aldrig faktureres, og skift til live-nøglen, når feltet virker. Reglerne for domæner — `www`, underdomæner med `*.` og porte — står i [dokumentationen for adressefeltet](https://grundfast.dk/docs/adressefelt#offentlige-noegler).

## Indlæs scriptet én gang

Læg script-tagget i `index.html` (eller i root-layoutet, hvis du bruger et framework med server-rendering). Nøglen kan stå på tagget, så alle felter i appen bruger den:

html

```
<script
  src="https://grundfast.dk/widget/v1/grundfast-adresse.js"
  defer
  data-key="gf_pub_live_..."
></script>
```

Uden `data-felt` hægter scriptet sig ikke på noget af sig selv — det er hookens opgave. Står tagget ikke i `index.html`, indlæser hooken nedenfor filen selv, første gang en formular med adressefelt vises — så skal nøglen gives som hookens tredje argument, fx fra `import.meta.env`.

## Hooken

`useGrundfastAdresse` tager en ref til inputfeltet og en callback. Den venter på scriptet, kalder `GrundfastAdresse.attach` og rydder op med `destroy()`, når komponenten afmonteres. Oprydningen er det, der gør hooken sikker i Strict Mode, hvor React monterer to gange under udvikling.

use-grundfast-adresse.ts typescript

```
import { useEffect, useRef, type RefObject } from 'react';

export interface GrundfastAdresse {
  betegnelse: string;
  adresselinje: string;
  vejnavn: string | null;
  husnr: string | null;
  etage: string | null;
  doer: string | null;
  postnr: string | null;
  postnrnavn: string | null;
  kommunekode: string | null;
  adgangsadresse_id: string;
  adresse_id: string | null;
  bfe_nummer: number | null;
  koordinat: { lon: number; lat: number } | null;
}

interface Widget {
  attach(
    input: HTMLInputElement,
    opts: { key?: string; onSelect?: (a: GrundfastAdresse) => void },
  ): { destroy(): void } | undefined;
}

declare global {
  interface Window { GrundfastAdresse?: Widget }
}

const SRC = 'https://grundfast.dk/widget/v1/grundfast-adresse.js';
let loading: Promise<Widget> | null = null;

/** Indlæs scriptet én gang, også hvis det ikke står i index.html. */
function loadWidget(): Promise<Widget> {
  if (window.GrundfastAdresse) return Promise.resolve(window.GrundfastAdresse);
  loading ??= new Promise((resolve, reject) => {
    const s = document.createElement('script');
    s.src = SRC;
    s.onload = () => (window.GrundfastAdresse ? resolve(window.GrundfastAdresse) : reject());
    s.onerror = () => { loading = null; reject(new Error('adressefeltet kunne ikke indlæses')); };
    document.head.appendChild(s);
  });
  return loading;
}

export function useGrundfastAdresse(
  ref: RefObject<HTMLInputElement | null>,
  onSelect: (a: GrundfastAdresse) => void,
  key?: string,
) {
  // Den nyeste callback, uden at feltet skal hægtes af og på ved hver render.
  const latest = useRef(onSelect);
  latest.current = onSelect;

  useEffect(() => {
    let field: { destroy(): void } | undefined;
    let cancelled = false;
    loadWidget()
      .then((w) => {
        if (cancelled || !ref.current) return;
        const onSelect = (a: GrundfastAdresse) => latest.current(a);
        // Uden key bruges data-key fra script-tagget.
        field = w.attach(ref.current, key ? { key, onSelect } : { onSelect });
      })
      .catch(() => { /* feltet virker som et almindeligt input */ });
    return () => {
      cancelled = true;
      field?.destroy();
    };
  }, [ref, key]);
}
```

## Komponenten

Formularens tilstand ejer du selv. Kunden kan stadig skrive i feltet som normalt, og når en adresse vælges, sætter `onSelect` resten af felterne. Etage og dør er allerede med i `adresselinje`, når bygningen har lejligheder.

AdresseFelter.tsx typescript

```
import { useRef, useState } from 'react';
import { useGrundfastAdresse } from './use-grundfast-adresse';

export function AdresseFelter() {
  const ref = useRef<HTMLInputElement>(null);
  const [adresse, setAdresse] = useState('');
  const [postnr, setPostnr] = useState('');
  const [by, setBy] = useState('');
  const [bfe, setBfe] = useState<number | null>(null);

  useGrundfastAdresse(ref, (a) => {
    setAdresse(a.adresselinje);
    setPostnr(a.postnr ?? '');
    setBy(a.postnrnavn ?? '');
    setBfe(a.bfe_nummer);
  });

  return (
    <fieldset>
      <label htmlFor="adresse">Adresse</label>
      <input id="adresse" ref={ref} value={adresse}
        onChange={(e) => setAdresse(e.target.value)} autoComplete="off" />
      <label htmlFor="postnr">Postnummer</label>
      <input id="postnr" value={postnr} onChange={(e) => setPostnr(e.target.value)} />
      <label htmlFor="by">By</label>
      <input id="by" value={by} onChange={(e) => setBy(e.target.value)} />
      <input type="hidden" name="bfe" value={bfe ?? ''} />
    </fieldset>
  );
}
```

Når scriptet skriver i adressefeltet, gør det det på samme måde som et tastetryk, så `onChange` kører, og React-tilstanden følger med. Callbacken sætter bagefter den endelige værdi. Har du `autoComplete="off"` på feltet, undgår du, at browserens egen udfyldning lægger sig oven på forslagslisten.

Next.js og andre frameworks med server-rendering

Hooken rører kun `window` og `document` inde i effekten, så den kører aldrig på serveren. Bruger du App Router, skal komponenten have `'use client'` øverst.

## Tjek nøglen fra en terminal

Før du leder i platformens indstillinger, kan du se, om nøglen og domænet passer sammen. Kaldet nedenfor er det samme, feltet laver. `Origin` er dit domæne, præcis som browseren skriver det:

bash

```
curl -G "https://api.grundfast.dk/v1/adresse/search" \
  --data-urlencode "q=Vestergade 10" \
  --data-urlencode "token=$GRUNDFAST_PUBLIC_KEY" \
  -H "Origin: http://localhost:5173"
```

Svarer det med adresser, er nøglen i orden. Et `403` med `origin … is not allowed` betyder, at domænet ikke står på nøglen — ret listen i dashboardet; det slår igennem inden for et halvt minut.

## Hvis der ikke kommer forslag

- Åbn browserens konsol (F12, fanen Console). Feltet skriver én linje, der begynder med `[grundfast-adresse]`, med årsagen fra API’et.
- `no public key` — scriptet blev indlæst uden `data-key`.
- Ingen linje og ingen liste — feltet blev ikke fundet. Tjek at selektoren i `data-felt` rammer feltet: skriv `document.querySelectorAll('…')` med den samme selektor i konsollen.
- Listen vises, men felterne udfyldes ikke — selektorerne i `data-postnr` og `data-by` rammer ikke noget. De slås op i samme formular som adressefeltet først, derefter på hele siden.

Feltet stopper aldrig formularen: fejler et kald, lukker listen, og kunden kan skrive adressen i hånden som før.

Var denne side nyttig?

## Ofte stillede spørgsmål

### Skal jeg installere en npm-pakke?

Nej. Adressefeltet er én fil, som du indlæser fra grundfast.dk, og hooken i guiden er de linjer, du skal bruge for at styre den fra React. Vil du låse en bestemt version, så brug den faste versions-URL, der står i dokumentationen for adressefeltet.

### Hvorfor monteres feltet to gange under udvikling?

React Strict Mode monterer komponenter, afmonterer dem og monterer dem igen for at finde effekter uden oprydning. Hooken kalder destroy i oprydningen, så der kun er ét felt tilbage. I produktion sker det kun én gang.

### Kan jeg bruge det med React Hook Form eller Formik?

Ja. Brug feltets ref fra biblioteket og sæt værdierne med bibliotekets egen setValue i onSelect. Feltet skriver også i inputtet som et tastetryk, så biblioteker, der lytter på input-hændelser, ser værdien.

### Kan jeg kalde API’et selv i stedet for at bruge scriptet?

Ja. Den offentlige nøgle virker også fra din egen fetch-kode mod adressesøgningen og de andre adresse-endpoints. Scriptet sparer dig for tastaturstyring, skærmlæserstøtte og trinnet med etage og dør.

## Prøv det på rigtige data

En gratis nøgle giver adgang til alle registre med samme svarform. Testnøgler faktureres aldrig, så du kan bygge færdigt, før du skifter til produktion.

[Kom i gang gratis](https://grundfast.dk/dashboard?mode=register) [Se alle guides](https://grundfast.dk/guides)

Gratis op til 5.000 kald/md · intet kreditkort · data fra Klimadatastyrelsen/Datafordeler, CC BY 4.0

Relateret

- [Adressefelt til checkout og formularer Dokumentation](https://grundfast.dk/docs/adressefelt)
- [Adressefelt med autocomplete i checkout Guide](https://grundfast.dk/adressefelt-i-checkout)
- [Søg efter en adresse GET /v1/adresse/search](https://grundfast.dk/docs/api/adresse-search)
- [Adresse-autocomplete i WooCommerce Guide](https://grundfast.dk/woocommerce-adresse-autocomplete)
- [Godkendelse og nøgler Dokumentation](https://grundfast.dk/docs/godkendelse)

På denne side

1. Opret en offentlig nøgle
2. Indlæs scriptet én gang
3. Hooken
4. Komponenten
5. Tjek nøglen fra en terminal
6. Hvis der ikke kommer forslag
7. Ofte stillede spørgsmål

[API-dokumentation](https://grundfast.dk/docs)[Alle guides](https://grundfast.dk/guides)[Spørg os](https://grundfast.dk/kontakt)
