An OpenRouter model selector

Search a live OpenRouter catalog, choose model settings, and read the selection in plain JavaScript.

The public OpenRouter model catalog needs no API key. This example emits the selected model and validated options. Your server remains responsible for authentication and sending requests. Discovery failures have a visible retry action.

Run it locally

Node.js 22.12 or newer. Download and extract the starter, then run these commands in its folder.

npm install
npm run dev

Download complete starter ↓ · All source files for agents

index.html

<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Models example</title>
<style>
* { box-sizing: border-box; }
body { max-width: 800px; margin: 64px auto; padding: 24px; font: 14px/1.5 system-ui; color: #242528; background: #fafafa; }
button { font: inherit; padding: 8px 12px; cursor: pointer; }
pre { overflow: auto; padding: 16px; border: 1px solid #ddd; border-radius: 8px; }
.prompt-bar {
  display: flex;
  align-items: flex-end;
  flex-wrap: wrap;
  gap: 12px;
  padding: 12px;
  border: 1px solid #d6d6d6;
  border-radius: 16px;
  background: #fff;
  color: #171717;
  --models-surface: #fff;
  --models-border: #d6d6d6;
  --models-radius: 7px;
  --models-control-height: 36px;
  --models-font-size: 13px;
}

.prompt-bar textarea {
  flex: 1 1 200px;
  min-width: 0;
  min-height: 36px;
  border: 0;
  padding: 8px;
  resize: vertical;
  background: transparent;
  color: inherit;
  font: 13px/1.5 system-ui, sans-serif;
}

.prompt-bar models-composer {
  margin-left: auto;
  max-width: 100%;
}
</style>
</head>
<body>
<h1>Choose your model</h1>
<div class="prompt-bar">
  <textarea aria-label="Prompt" placeholder="Ask anything…"></textarea>
  <models-composer></models-composer>
</div>
<p id="catalog-status" role="status">Loading models…</p>
<button id="catalog-retry" type="button" hidden>Retry catalog</button>
<pre id="selection" aria-label="Selected model" tabindex="0">Select a model or change an option to see its value.</pre>
<p>This example selects models. Connect your own request handler to send a prompt.</p>
<script type="module">
import { openRouterAdapter } from "@axelrock/models/providers";
import { defineModelsElements } from "./ui/index.ts";

defineModelsElements();
const selector = document.querySelector("models-composer");
const status = document.querySelector("#catalog-status");
const retry = document.querySelector("#catalog-retry");
selector.iconMode = "model-maker";
selector.groupBy = "author";
selector.groups = ["reasoning", "speed"];

selector.addEventListener("models-selection-change", (event) => {
  document.querySelector("#selection").textContent = JSON.stringify(event.detail, null, 2);
});
selector.addEventListener("models-model-clear", () => {
  document.querySelector("#selection").textContent = "No model selected.";
});

async function loadCatalog() {
  retry.hidden = true;
  status.textContent = "Loading models…";
  try {
    const catalog = await openRouterAdapter.discover({ signal: AbortSignal.timeout(10000) });
    selector.catalogs = [catalog];
    status.textContent = catalog.models.length ? "Choose a model to begin." : "No models are available. Try again.";
    retry.hidden = catalog.models.length > 0;
  } catch {
    status.textContent = "Could not load models. Check your connection and retry.";
    retry.hidden = false;
  }
}
retry.addEventListener("click", loadCatalog);
loadCatalog();
</script>
</body>
</html>

Make it yours

Keep the UI files in your project. Change the styles, choose which option groups appear, and handle the selection event. Provider API keys belong on your server.

Try the Models playground →