Alap v3 Beta 4 — Rethinking the Humble Link

For 35 years, the anchor tag has worked the same way: one link, one destination, chosen by the author. It's so fundamental we stopped questioning it.

But what if a link could be an invitation instead of a demand — offering the reader a curated set of choices?

That's Alap.

Built on What Already Works

Alap extends the idea of what we would expect from a link. We get dynamic menus via a simple attribute and a lightweight expression language. No proprietary formats, no lock-in, no framework required.

The project is open source (Apache 2.0) and published across five package registries (npm, and language parsers in: crates.io, PyPI, Go Modules, and Packagist).

An Expression Language for Links

Instead of hardcoding destinations, Alap uses a tag-based query language. You can use regular anchors with Alap attributes or the <alap-link> web component — your choice:

<!-- Standard anchor with Alap attributes -->
<a class="alap" data-alap-linkitems=".coffee">coffee shops</a>

<!-- Or as a web component -->
<alap-link query=".coffee">coffee shops</alap-link>

<!-- All bridges -->
<alap-link query=".bridge">bridges</alap-link>

<!-- Intersection — NYC bridges only -->
<alap-link query=".nyc + .bridge">NYC bridges</alap-link>

<!-- Macro — a named set of favorites -->
<alap-link query="@favorites">favorites</alap-link>

Tags, intersections, unions, subtraction — compose your menus from a shared link library. Macros let you name and reuse common sets. The grammar is small enough to learn in minutes, powerful enough to express complex selections.

Alap can also pull links from the live web and from AT Protocol (Bluesky), so your menus can mix curated and dynamic sources:

<!-- Live search — Open Library JSON API -->
<alap-link query=":web:books:decentralization:">Books: "decentralization"</alap-link>

<!-- AT Protocol — recent posts from a Bluesky account -->
<alap-link query=":atproto:feed:eff.org:limit=3:">EFF — recent posts</alap-link>

<!-- Mix all three: static tags + web API + AT Protocol -->
<alap-link query="(.orgs *limit:2*), (:web:books:decentralization:), (:atproto:feed:eff.org:limit=3:)">
  Decentralization: orgs + books + EFF posts
</alap-link>

The Menu Is Just the Beginning

Alap separates parsing from presentation. The expression engine resolves a set of links — what you do with them is up to you.

The default renderer shows a dropdown menu. Swap in the lightbox renderer and the same config can drive a carousel. The parser doesn't care. Build a side panel, a card grid, a command palette — the resolved links are just data.

Works Everywhere You Do

Framework Adapters

Alap ships adapters for most major frameworks. Each one provides idiomatic components, hooks, and state management — not a wrapper around a generic widget.

AdapterComponentsNotable
DOM<alap-link> web componentZero dependencies, no build step
ReactAlapProvider, AlapLink, useAlap()Hooks and context
VueAlapProvider, AlapLink, useAlap()Composition API
SvelteAlapProvider, AlapLink, useAlap()Svelte 5
SolidAlapProvider, AlapLink, useAlap()Fine-grained reactivity
QwikAlapProvider, AlapLink, useAlap()Resumable, lazy by default
Alpinex-alap directiveNo build step needed
AstroAlapLink.astro, AlapSetup.astroWorks in .astro and MDX

React, Vue, Svelte, Solid, and Qwik adapters support three rendering modes: DOM, Web Component, and Popover API. Every adapter includes keyboard navigation, ARIA accessibility, click-outside dismissal, and a compass-based placement engine.

Integrations

Drop Alap into your static site generator or documentation platform with zero-config setup.

IntegrationPlatformWhat It Does
astro-alapAstroAuto-injects web component setup; optional remark plugin for .md and .mdx
eleventy-alapEleventyDual-mode: static build-time resolution (plain <ul>) or interactive web components; shortcode + filters
hugo-alapHugoHugo module with alap shortcode; full expression syntax client-side
next-alapNext.js'use client' re-exports; AlapLayout for app/layout.tsx; optional MDX support
nuxt-alapNuxt 3Client plugin factory; Vue component re-exports; Nuxt Content markdown support
vitepress-alapVitePressVite plugin for custom element registration; use <alap-link> directly in markdown
qwik-alapQwik CityVite plugin with auto-injection; supports both web components and Qwik components

Plugins

Content-level transforms for CMS pipelines, rich text editors, and markdown toolchains.

PluginEnvironmentWhat It Does
remark-alapMarkdown (unified)Transforms [text](alap:query) links to <alap-link> web components
rehype-alapHTML (unified)Transforms <a href="alap:query"> for headless CMS content (Contentful, Sanity, WordPress REST, etc.)
mdxMDX / ReactRemark plugin + React provider; emits <AlapLink> components resolved via context
tiptap-alapTiptap / ProseMirrorInline <alap-link> nodes with insert/update/remove commands; Mod-Shift-A shortcut
WordPressWordPress (PHP)[alap query=".tag"] shortcode; SQLite-based; file config, no database tables

Server-Side Parsing in Your Language

I wrote idiomatic parsers in four languages — not mechanical ports, but implementations that feel native to each ecosystem.

LanguagePackageCapabilities
Rustalap (crates.io)Expression parsing, config merging, URL sanitization
Pythonalap-python (PyPI)Expression parsing, config merging, ReDoS guard
Goalap-go (Go modules)Expression parsing, config merging, URL sanitization
PHPdanielsmith/alap (Packagist)Expression parsing, config merging, ReDoS validation

All four implement the same expression grammar — item IDs, tag queries, operators, macros, regex search, and grouping — with full test suites. Use them for server-side resolution, build-time rendering, or API endpoints.

Tested, Not Just Written

977 tests across 45 files — core parser, every framework adapter, integrations, plugins, security (URL sanitization, ReDoS guards, config validation). This is a beta because the API could shift, and I feel like polishing examples, filling in some gaps, and doing the web site.

Proof-of-Concept Editors

I created example config editors in many frameworks — React, React ShadCN, Vue, Svelte, Solid, Alpine, Astro, and a React Design reference — to prove that the same editing experience works consistently across stacks.

Each editor supports JSON import/export as files and can call any of our example servers (Axum, Flask, Go, Express, PHP) for server-side resolution. They share the same UI: build a link library, write test queries, and preview resolved menus — all with drag-and-drop link import and client-side metadata extraction.

The point isn't that you need all of these editors. It's that any framework can drive the same config surface with the same results.

See It in Action

Many live examples — DOM, Web Components, React, Vue, Svelte, Solid, Qwik, Alpine, Astro, htmx, CMS content, markdown, and a lightbox renderer. Each one is self-contained and viewable in the examples gallery.

Browse the examples Documentation GitHub npm
npm install alap

Author Note

I'm Daniel Smith, and I've been thinking about aspects of this for 30 years. I published an early version of this back in 2012, and rewrote it in 2022, with an eye towards Vue and React. Version 3 is a complete rewrite in TypeScript. I worked in tandem with Claude Code to get this going, and I assure you this is no vibe coding project :)