Uniweb

Linking

A robust linking system that survives content reorganization and works seamlessly in single-page applications.

In traditional websites, links are tied to URL paths:

[Installation Guide](/docs/getting-started/installation)
[Contact Section](/about#contact-form)

This works until you reorganize your content. Move "Installation" under a new "Setup" section? Every link breaks. Rename a page? Broken links. Reorder sections on a page? Hash links stop working.

Documentation sites are particularly vulnerable—content restructuring is routine as projects evolve. Maintaining hundreds of internal links becomes a burden that discourages improvement.

Stable References

Uniweb solves this with a stable reference system that decouples a page's identity from its location in the site hierarchy.

[Installation Guide](page:installation)
[Contact Section](page:about#contact)

These links resolve to the correct URL regardless of where the content lives. Move "Installation" anywhere in your site—the link still works.

How It Works

Section IDs (Automatic)

Every section gets a stable ID derived from its filename:

pages/home/
├── 1-hero.md         → #section-hero
├── 2-features.md     → #section-features
├── 2.5-testimonials.md → #section-testimonials
└── 3-cta.md          → #section-cta

The numeric prefix is for ordering only—the ID comes from the name part. You can freely reorder sections (change 2-features.md to 5-features.md) without breaking any links to #section-features.

Override when needed via frontmatter:

---
type: ContactForm
id: contact
---

Page IDs (Optional)

By default, pages are referenced by their route path, and the homepage's route is /:

[About Us](page:about)
[API Docs](page:docs/api)
[Home](page:/)

A page is named by its route or by its ID, never by its folder: the homepage lives in pages/home/ but is page:/, unless you give it an ID.

For pages that might move, add an explicit ID in page.yml:

# pages/docs/getting-started/installation/page.yml
id: installation
title: Installation Guide

Now page:installation resolves correctly even if you later move the page to pages/setup/install/.

Sites you also edit in the Uniweb app

The app links a page by an ID it creates, a code like 3f58142d, which it keeps in the page's page.yml. uniweb pull writes the app's links as the page's path, [AI](page:ai), and uniweb push stores them by the code again, so they keep working when the page is renamed in the app. An ID you chose yourself, like installation, is written as you wrote it. Leave the id: the app put in a page.yml: it is how a push knows which page a path names.

The page: protocol supports pages, sections, and combinations:

SyntaxResolves To
page:about/about
page:docs/api/docs/api
page:// (the homepage)
page:/#features/#section-features
page:about#contact/about#section-contact
page:installationRoute of page with id: installation
page:installation#requirementsSame page, scrolls to requirements section

In Markdown Content

For setup instructions, see the [Installation Guide](page:installation).

Have questions? Visit our [contact form](page:about#contact).

Docs kept in a repository link to each other by file, because that's what works on GitHub and in an editor. They work on the site too. A relative link to a markdown file leads to the page that file became, built or pushed, and its anchor to that page's heading, whether it's GitHub's anchor or the site's:

[Deploying](./deploying.md)                  <!-- /docs/guides/deploying -->
[Setup](../guides/deploying.md#first-setup)  <!-- /docs/guides/deploying#first-setup -->

A file that didn't become a page (a README.md, one that's been moved) stays as written, and the build names it, since it leads nowhere. Use page: for a link that should survive moving the file.

Cross-Page Scrolling

When navigating to a page with a hash (e.g., page:about#contact), the runtime:

  1. Navigates to the page via client-side routing
  2. Waits for the page to render
  3. Smoothly scrolls to the target section

This works in both SPA mode (development) and with pre-rendered pages (production).

When to Use Explicit IDs

Section IDs are automatic—you rarely need to override them. Do so only when:

  • The filename doesn't reflect the section's purpose
  • You want a shorter/cleaner hash URL
  • Multiple sections could have similar auto-generated IDs

Page IDs are optional. Add them when:

  • The page is frequently linked to from other pages
  • You anticipate reorganizing your site structure
  • You want URLs to survive localization (same ID across /en/ and /es/)

Comparison with Other Approaches

ApproachProsCons
Path-based linksSimple, familiarBreak on reorganization
Link checking toolsCatch broken linksFix after the fact, maintenance burden
CMS-managed linksCentralizedRequires CMS, vendor lock-in
Uniweb stable IDsAutomatic, survives changesRequires page: protocol

Best Practices

  1. Use page: for internal links—plain paths still work but don't get the stability benefits

  2. Let section IDs be automatic—meaningful filenames like 2-features.md give you #section-features for free

  3. Add page IDs proactively for high-traffic pages in documentation sites

  4. Keep IDs short and descriptive—they appear in URLs when users share links

  5. Don't change IDs once content is published—they're part of your public API


See Also