SystemetRådgivningUdviklingArbejdePriser
Tilbage til guides
5 min. · CMS & INDHOLD

Dynamiske CMS-links: hvorfor dit 'Link'-felt er en tidsbombe

De fleste CMS-implementeringer lader forfattere indsætte rå URL'er i et tekstfelt. Den dag, nogen omdøber en slug, 404'er halvdelen af din navigation i stilhed. Her er modellen, der gør det umuligt.

På denne side
  • Tirsdag morgen-hændelsen
  • Standarden: et tekstfelt
  • Hvorfor det bryder sammen i skala
  • Den rigtige model: links som referencer
  • Forælder/barn-relationer som kilden til sandheden
  • Sådan løste jeg det i Contentful
  • Migrationssti fra tekstfelt-links
Opdateret 2026-04
TL;DR
  • Et URL-input som tekstfelt har ingen validering, ingen autocomplete og ingen referentiel integritet.
  • Slug-ændringer, locale-varianter, sletninger og forælder/barn-flytninger bryder alle links i stilhed, som du ikke kan finde uden at crawle sitet.
  • Modellér linkfelter som referencer til Page-entries, ikke strenge. Beregn URL'en ud fra sidens position i træet.
  • Forælder/barn-relationer hører hjemme i CMS'et, med én kilde til sandheden for stien.
  • Jeg løste det i Contentful med en self-referencing Page-type og en path resolver. Samme mønster virker i Sanity, Strapi og Storyblok.

Tirsdag morgen-hændelsen

En tirsdag morgen omdøber en marketing-medarbejder en kampagneside i CMS'et. De ændrer /kampagner/forar-2024 til /kampagner/forar-launch. Tre nav-punkter, to forsidekort og en betalt ads-landingsside peger nu på en 404. Ingen opdager det i elleve dage. Betalt trafik blev ved med at ankomme hele tiden.

Det er ikke en forfatterfejl. Det er en modelfejl. CMS'et gav dem et tekstfelt og en fodgun.

Standarden: et tekstfelt

I den gennemsnitlige CMS-opsætning er "link til en anden side" implementeret som et string-felt. Forfatteren forventes at huske URL'en, taste eller indsætte den korrekt, og opdatere hver reference i hånden, hvis den nogensinde ændrer sig.

Der er ingen validering af, at URL'en eksisterer. Ingen advarsel, hvis målet er upublished, slettet eller flyttet. Ingen måde for CMS'et at fortælle dig seks måneder senere: "disse 47 steder linker til en side, der ikke længere eksisterer." Bureauet, der byggede det, leverede til tiden. Omkostningen blev udskudt til forfatteren og til din trafik.

Hvorfor det bryder sammen i skala

Det kræver ikke et redesign for at bryde sammen. De daglige operationer i et content-team bryder det:

  • Slug-ændringer. SEO omdøber en side af keyword-grunde. Hvert tekstfelt-link, der pegede på den gamle slug, brækker.
  • Locale-varianter. Den engelske forfatter taster /about. Den danske version er /da/om-os. Halvdelen af de lokaliserede sider linker ud af locale.
  • Forælder/barn-flytninger. En side flyttes under en ny forælder. Dens fulde sti ændrer sig. Hvert link til den er nu forkert.
  • Sletninger og unpublish. En side tages ned. Hvert tekst-link til den peger på en 404 for evigt.
  • Domæne- eller scheme-ændringer. Trailing slashes, http vs. https, www vs. apex. Vælg en URL-detalje og find den forfatter, der tastede den anderledes.
Et tekstfelt til "link til en side" er en database uden foreign keys. Det vil svigte dig i det øjeblik, dit content-team udfører normalt arbejde.

Den rigtige model: links som referencer

Stop med at gemme URL'er. Gem en reference til den entry, forfatteren vil linke til, og beregn URL'en fra entryen. Linkfeltet på hver indholdstype bliver:

  • En reference til en Page-entry (eller hvilke entry-typer der nu er linkbare).
  • En valgfri override-label, så forfattere kan sige "link til denne side, men kald den 'Læs historien'."
  • En valgfri anchor til in-page-jumps.

Forfatteren behøver ikke længere kende URL'er. De søger på sidetitel, vælger entryen, og systemet garanterer, at linket vil resolve, så længe siden eksisterer.

Forælder/barn-relationer som kilden til sandheden

Den anden halvdel af løsningen er at modellere navigationshierarkiet i selve CMS'et. I de fleste opsætninger lever URL'en for en side tre steder: et slug-felt, en redaktørs hukommelse, og en hånd-vedligeholdt navigationsmenu. Tre kilder, ingen enighed.

Modellen, der holder:

  • Ét felt på siden: slug-segmentet for kun den side (fx forar-launch, ikke den fulde sti).
  • Et self-reference-felt: forælder, der peger på siden ovenover i træet.
  • En beregnet fuld sti, afledt ved at gennemløbe fra siden til roden og samle slug-segmenterne.
  • Navigationsmenuen genereres fra dette træ, vedligeholdes ikke i hånden.

Når en side flyttes under en ny forælder, opdateres hver URL under den. Hvert reference-link til den fortsætter med at resolve, fordi referencer gemmer entry-ID'er, ikke stier.

Sådan løste jeg det i Contentful

Mønsteret nedenfor er den præcise model, jeg nu bruger på hvert Contentful-projekt. Den er lille. Den er forskellen mellem et CMS, der ældes godt, og et, der forfalder til en kirkegård af brudte links.

Page-indholdstypen

En enkelt Page-type dækker hver URL på sitet, med en self-reference til hierarki.

Contentful · Page
title: Symbol            // krævet, indekseret
slug:  Symbol            // krævet, kun segment ("forar-launch")
parent: Link<Entry<Page>> // valgfri, self-reference
locale: Symbol           // krævet (en, da, ...)
sections: Array<Link<Entry<Section>>>
seo:    Link<Entry<Seo>>

// Validering:
//  - slug er unik blandt sider med samme forælder + locale
//  - forælderen skal være af typen Page og samme locale
//  - cycle-check: en side kan ikke være sin egen forfader

Linkfelt brugt alle andre steder

Hver anden indholdstype, der har brug for et link — CTA'er, nav-punkter, kort, rich-text-inline-links — bruger samme PageLink-form. Aldrig en URL-streng.

Contentful · PageLink
page:   Link<Entry<Page>> // krævet
label:  Symbol            // valgfri override
anchor: Symbol            // valgfri, fx "pricing"
rel:    Symbol            // valgfri, fx "nofollow"

// Eksterne links er en separat type (ExternalLink),
// så modellen selv forhindrer, at man taster en URL i et felt,
// der skulle være internt.

Path resolver

En lille serverfunktion forvandler enhver Page til dens fulde URL ved at gennemløbe forælder-kæden. Resultatet caches pr. side og invalideres af Contentful-webhooks, når en sides slug eller forælder ændres.

ts
type PageRef = { sys: { id: string }; fields: { slug: string; parent?: PageRef; locale: string } }

export function resolvePath(page: PageRef): string {
  const segments: string[] = []
  let current: PageRef | undefined = page
  while (current) {
    segments.unshift(current.fields.slug)
    current = current.fields.parent
  }
  const localePrefix = page.fields.locale === "en" ? "" : `/${page.fields.locale}`
  return `${localePrefix}/${segments.join("/")}`.replace(/\/+/g, "/")
}

Hvad redaktøren ser

Når en forfatter indsætter et link, søger de på siden ved titel i Contentfuls reference-picker. CMS'et viser den resolvede URL som et read-only preview. Hvis målsiden er upublished eller slettet, viser Contentful en broken-reference-warning direkte i editoren — før ændringen går live, ikke elleve dage senere.

At omdøbe en slug, flytte en side under en ny forælder eller unpublish'e en side opdaterer nu hvert link automatisk, fordi intet link nogensinde gemte URL'en til at starte med.

Migrationssti fra tekstfelt-links

Du behøver ikke bygge om for at adoptere det. Migrationen er mekanisk:

  1. Tilføj den nye PageLink-reference-type ved siden af det eksisterende tekst-URL-felt. Slet ikke det gamle endnu.
  2. Kør et engangsscript, der gennemløber hver entry, parser URL-strengen, finder den matchende Page ved sti og skriver referencen. Log de umatchede URL'er.
  3. Triagér den umatchede liste. De fleste vil være eksterne links (flyt dem til ExternalLink) eller links til sider, der ikke længere eksisterer (de var allerede brudte — nu ved du det).
  4. Stil en redirect-tabel op til de 404'er, du fandt. Den er også CMS-styret: en Redirect-type med from-streng og to som en PageLink.
  5. Skjul det gamle tekst-URL-felt i editoren. Fjern det efter én publiceringscyklus.

Det her er ikke Contentful-specifikt

Præcis den samme model virker i Sanity (references + parent self-reference), Storyblok (story links), Strapi (relations) og hvert headless CMS, der understøtter referencer. Pointen er strukturel, ikke vendor-specifik.
What to actually do
  • Auditér hvert linkfelt i dit CMS. Hvis det accepterer en URL-streng, er det en fremtidig 404.
  • Erstat interne linkfelter med referencer til Page-entries. Eksterne links går i en separat type.
  • Modellér forælder/barn som en self-reference på Page-typen. Beregn stien; gem den ikke.
  • Generér navigationsmenuen fra træet, ikke fra en hånd-vedligeholdt liste.
  • Tilføj en CMS-styret redirect-tabel under migrationen for at fange de URL'er, du ikke kan mappe.

Vil du have denne slags vurdering på dit projekt?

Jeg læser hver e-mail inden for én arbejdsdag. Tag et projekt, et tilbud eller et system, du sidder fast i.

Se Enterprise Starter
Relaterede guides
  • 9 min.

    15 ting hvert Contentful enterprise-projekt får galt i de første 6 uger

    De 15 produktions-huller hvert enterprise Contentful + Next.js-build rammer i de første seks uger — og hvordan du lukker hver enkelt uden at brænde et sprint. En pre-kickoff-checkliste til tech-leads på en Contentful enterprise starter.

  • 13 min.

    REST + GraphQL-hybrider til multi-locale CMS-drevne sites

    Hvorfor hverken kun REST eller kun GraphQL er det rigtige valg til et enterprise multi-locale CMS-site, og hvordan du splitter efter ansvar i stedet. Inkluderer cirkulære referencer på fulde REST-payloads, bundle-omkostningen ved GraphQL på klienten, beslutningsmatricen pr. kaldsted, den samlede fetcher, granulære cache-tags, blok-som-fragment-mønsteret, locale-fallback i ét round-trip, Live Preview der overlever, Server Actions til CMA-skrivninger, Algolia Sync API-undtagelsen og migrationsrækkefølgen.

  • 3 min.

    CMS-styret analyse: stop med at betale udviklere for hver tracking-ændring

    Sådan strukturerer du dit CMS, så marketing kan tilføje tracking-hændelser uden udviklerindblanding. Spar sekscifrede DKK-beløb over sitets levetid.

Uafhængig teknologirådgivning og udvikling. København, Danmark.

Njalsgade 21F, 2. sal, København

CVR 45 44 13 93

nicklas@ceero.eu

WhatsApp +45 31 33 25 99

Arbejde

  • Enterprise Starter
  • Rådgivning
  • Udvikling
  • Arbejde
  • Kontakt

Ressourcer

  • Artikler
  • Guides
  • Gratis værktøjer
  • Om os

Andre steder

  • Sparro
  • Invigilo
  • e-sign
  • Privatlivspolitik

© 2026 Ceero ApS · CVR DK45441393. Alle rettigheder forbeholdes.

Bygget i København med Next.js, Contentful og nul konsulent-bullshit.