Type: link

A link resource is a redirect node in the doc tree. Visiting its URL 302s to an external target. Inside a parent list or board it renders as a normal row (icon + name + desc) — clicking the row navigates to the target. Implementation: src/content/link.rs.

Purpose

Surface external resources inside the FDS doc tree without copying their content. Typical uses: cooperation-partner links, upstream specs, social-media or chat-platform URLs that belong in the navigation but aren’t ours to host.

ctx.json shape

{
    "type": "link",
    "name": {
        "en": "Anthropic",
        "ja": "Anthropic",
        "zh": "Anthropic"
    },
    "desc": {
        "en": "Maker of Claude.",
        "zh": "Claude 的开发商。"
    },
    "icon": "/static/icons/anthropic.png",
    "url": "https://www.anthropic.com/"
}

Field reference

FieldTypeRequiredNotes
typestring — must be "link"yesDiscriminator.
nameLangDictyesDisplay label. See Common: Header and LangDict.
iconstringoptionalURL or path. Cleared on save when empty.
descOptionLangDictoptionalUsed as meta description and shown by parent list/board views.
bannerOptionLangDictoptionalBanner image URL — only relevant on the fallback view (see “Render flow”).
urlstringyesTarget URL. Single string, not a LangDict. Stored at the top level as url (not target).

Note: url is a plain string. The implementation in link.rs calls ctx.get("url").string() and stores it on Link::target — there is no per-language URL machinery. If you need different URLs per language, model it as separate link resources (or, more commonly, use a parent list to switch on language).

File-on-disk layout

<resource_folder>/
    ctx.json

That’s it. No .md, no body, no extra files.

Render flow

The dispatcher (content.rs::render, "link" arm) handles link specially:

  1. Read the resource via Link::read.
  2. If url (target) is non-empty — return a 302 redirect to the URL via redirect_response. The page chrome is never rendered.
  3. If url is empty — render the fallback body:
<div class="alert alert-warning my-3">No URL configured for this link.</div>

This is intentional: an empty url is a misconfiguration, and the warning surfaces it to whoever opened the page (typically an admin). Normal visitors never see the fallback because a properly-configured link always 302s before render() is reached.

When target is non-empty but render() is somehow invoked (e.g. inside a parent list’s row rendering), the fallback path emits <p>Redirecting to <a href="{url}">{url}</a>...</p>.

Editor capabilities

?view=edit (see Route: edit) exposes:

  • Rename for the active language (rename).
  • Set/clear icon (set_icon — empty string clears).
  • Set/clear desc for the active language (set_desc — empty clears that language; clearing the last language drops the whole desc key from disk).
  • Set the target URL (set_target). The URL is a single string, not per-language.

Active language comes from the navbar dropdown.

Download behavior

link does not support ?view=download. type_supports_download returns true only for singlemd, mdbook, and file. A ?view=download request on a link falls through to the standard dispatcher and 302s to the target URL like any other link visit. See Route: download.

Save semantics

Link::save() rebuilds ctx.json from scratch (does not preserve unknown keys). The serialised JSON contains:

  • type: "link"
  • The Header (name always; icon/desc/banner only when set)
  • url — the target string (may be empty)

Rebuilding from scratch is deliberate: it ensures that clearing the icon or removing all desc languages actually deletes those keys from disk, instead of leaving stale entries from the previous read. Empty-string url is written verbatim — an empty link is the placeholder state for a newly-created resource awaiting configuration.

Use cases

  • Cooperation-partner links in the partners list.
  • External resources surfaced inside an mdbook-adjacent table of contents.
  • Quick navigation aliases for documents that live outside the doc server.

Gotchas / common edit mistakes

  • Adding a per-language url dict. The reader calls .string() on the value — a dict will stringify in a way that won’t redirect anywhere useful. Use a single string. Localise the click destination by using a parent list or by creating sibling link resources and selecting one per language at the parent level.
  • Missing scheme. redirect_response sends the URL verbatim. www.example.com (without https://) is interpreted as a same-origin path. Always prefix with https://.
  • Empty url in production. Visitors get the yellow “No URL configured” warning page. Either fill in the URL or delete the resource.
  • Trying to download a link. There is no body to download. The route falls through to the redirect.
  • Don’t invent extra fields. Link::save() writes only type, the Header, and url. Anything else added by hand survives until the next save, then is silently dropped.