Generate Unique IDs Per Request in Astro
In a recent Astro.js project, I had a Form component that could be used multiple times on the same page. Each form had labels and inputs that needed unique but matching id attributes.
Inspired by Alpine.js’s $id magic function, I came up with a solution to do the same when doing server rendering in Astro.
My solution works by injecting a new instance of an ID generator into Astro.locals inside the onRequest hook. This function is called once per request, before the page is rendered, which gives us a unique ID generator for each request.
First, let’s add the type definition for the function:
src/env.d.ts:
declare namespace App {
interface Locals {
$id: (prefix: string) => string
}
}
Then, in src/middleware.ts, we’ll add this function in the onRequest hook:
import { defineMiddleware } from 'astro:middleware'
export const onRequest = defineMiddleware(async (context, next) => {
context.locals.$id = (() => {
// The map to store how many times each prefix has been used in this request.
const map = new Map<string, number>()
return (prefix: string) => {
const count = (map.get(prefix) ?? 0) + 1
map.set(prefix, count)
return `${prefix}-${count}`
}
})()
return await next()
})
Now, in our forms, we can call Astro.locals.$id() with the id prefix we want.
---
const { $id } = Astro.locals
const nameId = $id('name')
---
<form>
<label for={nameId}>Name</label>
<input id={nameId} />
</form>
When this form is rendered for the first time in a request, nameId will be "name-1". On the second render of the component in the same request, it will be "name-2", and so on. Every request gets its own unique IDs starting from 1.