←All posts

Immediate UI Mode: Frictionless Passkey Sign-In Ships in Chrome

Chrome 149 promotes WebAuthn immediate mediation to stable as Immediate UI mode, letting you surface a passkey or password picker the instant a user taps Sign In — and fail silently when there's nothing to offer. Here's how to wire it up with working code.

Immediate UI Mode: Frictionless Passkey Sign-In Ships in Chrome

Sign-in is where you lose people. A returning customer taps Sign In, gets a form, tries to remember which email they used, fumbles a password, and half of them never make it to the thing they came to do. Passkeys were supposed to fix this, but the timing has always been awkward: show the account picker too eagerly and you interrupt people who don't have a credential; wait for them to click into a field and you're back to a form.

Chrome 149 closes that gap. It promotes a capability that spent time in an origin trial as WebAuthn immediate mediation to stable, now under the name Immediate UI mode. The idea is simple and, for anyone shipping an authenticated product, worth an afternoon of integration work: at a real sign-in moment, ask the browser whether it has a usable credential. If it does, the browser shows an immediate login dialog. If it doesn't, the promise rejects quietly and your normal sign-in UI carries on as if nothing happened.

What "immediate" actually means

WebAuthn already gives you two ways to request a credential, and it helps to place the new one against them.

Modal mediation is the classic navigator.credentials.get() call. It always shows browser UI — even when there's no matching passkey, the user gets a dialog that may dead-end. Great for an explicit "sign in with a passkey" button, disruptive if you fire it speculatively.

Conditional mediation (passkey autofill) is the other end. You annotate a username field with autocomplete="username webauthn", call get() with mediation: 'conditional', and passkeys appear inside the autofill dropdown alongside saved passwords. It never interrupts, but it also requires the user to focus the field and engage with autofill.

Immediate UI mode sits between them. It behaves like the preferImmediatelyAvailableCredentials option that Android and iOS have had for a while: present a picker only if credentials are already on the device, and bail instantly otherwise. You get the proactivity of a modal prompt without the dead-end when the user is new. Per the Chrome documentation, the eligible credentials on Chrome are passkeys stored in a provider such as Google Password Manager, Windows Hello, or iCloud Keychain, plus passwords saved in Google Password Manager.

Feature-detect before you call

Immediate UI mode is single-engine surface today — as of May 2026, Chrome is the only browser that supports it. So detection isn't optional. The right check is PublicKeyCredential.getClientCapabilities(), which reports an immediateGet capability:

async function supportsImmediateUI() {
  if (!window.PublicKeyCredential?.getClientCapabilities) {
    return false;
  }
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  return capabilities.immediateGet === true;
}

If it returns false, fall back to whatever you already ship — a password form, conditional UI autofill, email magic links, or social login. Treat the presence of Immediate UI mode as a progressive enhancement, not a dependency. For teams that want a consistent API across browsers while support catches up, there's a community WebAuthn polyfill that smooths over the gaps.

Requesting credentials

The call itself is a normal navigator.credentials.get() with one new field: uiMode: 'immediate'. Adding password: true lets the same dialog offer managed passwords, not just passkeys, which matters if your users haven't all migrated yet.

signInButton.addEventListener('click', async (event) => {
  event.preventDefault();

  try {
    const credential = await navigator.credentials.get({
      password: true,
      publicKey: {
        challenge: serverGeneratedChallenge, // fresh, per-request, from your server
        rpId: 'example.com',
      },
      uiMode: 'immediate',
    });

    // A credential came back — verify the assertion on your server.
    await completeSignIn(credential);
  } catch (error) {
    if (error.name === 'NotAllowedError') {
      // No local credential, or the user dismissed the dialog.
      showFallbackSignIn();
    } else {
      throw error;
    }
  }
});

The critical detail is the catch. Immediate UI mode signals "nothing to show" by rejecting with a NotAllowedError — the same error you get if the user dismisses the dialog. That rejection is your cue to render the ordinary sign-in path. Handle it explicitly; don't let it bubble up as an unhandled failure, and don't treat it as an outage.

One thing worth flagging for anyone migrating: if you were in the origin trial, mediation: 'immediate' no longer triggers the feature. A November 2025 spec update moved it to the dedicated uiMode: 'immediate' field, so old code silently stops working. Grep for it.

Two flows that earn their keep

The Chrome team calls out two patterns, and both map cleanly onto real products.

The dedicated sign-in button

The user clicks Sign In. You call get() with uiMode: 'immediate'. If the browser finds a passkey or saved password, it shows the immediate login dialog and the user picks an account in one tap. If not, the promise rejects and you route to your standard sign-in page. The win is that returning users get a one-tap path while newcomers see no jarring, empty prompt — the same button does the right thing for both.

Sign-in before checkout

Ecommerce is the sharper use case. A guest hits Checkout and normally faces a fork: log in or continue as guest. Fire Immediate UI mode at that moment and a returning customer who happens to have a passkey gets offered it right there, collapsing account sign-in into the checkout tap. If they have no credential, nothing changes — they land on the guest checkout form exactly as before. You've added a fast lane without adding a speed bump for anyone else.

The guardrails Chrome enforces

Because this API can probe for the existence of credentials, Chrome wraps it in constraints designed to prevent abuse and silent fingerprinting. Know them before you architect around the feature, because several of them will reject your call outright:

  • A user gesture is required. The call must follow a genuine interaction like a click. Notably, it does not consume that activation, so you can still do other gesture-gated work in the same handler.
  • Incognito always rejects. Requests in incognito or private windows throw NotAllowedError unconditionally, so those users always get your fallback.
  • No allowlists. Passing a non-empty allowCredentials list throws NotAllowedError. Immediate mode is for "any credential you have for this site," not for targeting a specific one — a deliberate anti-tracking measure.
  • No programmatic cancellation. You can't use an AbortSignal to dismiss the immediate login dialog once it's up. Design your flow so you don't need to tear it down mid-prompt.

These aren't edge cases to paper over; they're the shape of the feature. If your current sign-in logic relies on an allowlist or an abort controller, you'll refactor those paths for the immediate flow.

Where this fits right now

Be clear-eyed about the state of play. This is stable in Chrome, but it isn't Baseline — other engines don't implement it yet, and the eligible-credential rules are Chrome-specific. That's fine, because the feature is built to degrade well. The entire value proposition is "enhance when the credential exists, disappear when it doesn't," which is exactly the posture you want for a not-yet-universal API.

Practically, that means the integration is low-risk. You're not betting your login on it; you're layering a faster path on top of a login you already have. Browsers without support, users without a saved credential, and private-window sessions all fall through to the flow you ship today.

Practical takeaways

If you run an authenticated product — a SaaS dashboard, a storefront, a client portal — Immediate UI mode is one of the cheaper conversion wins available right now, because the surface area is small and the fallback is your existing code. Wire it up in this order: feature-detect with getClientCapabilities() and bail to your current flow when immediateGet is absent; call navigator.credentials.get() with uiMode: 'immediate' and password: true behind a real click; and treat NotAllowedError as the ordinary "no credential" branch that renders your normal sign-in, not as an error to log and forget.

Put it on the highest-intent moments first — the Sign In button and the Checkout tap — where shaving a form off the path pays back immediately. Keep your password form, your conditional-UI autofill, and your social logins exactly where they are; this sits on top of them. As passkey adoption climbs and more engines implement the API, the fast lane you build today only gets more traffic — and the teams that instrument their sign-in funnel now will know exactly what it's worth.

Sources: Streamlined sign-in: Immediate UI mode is now available — Chrome for Developers · Immediate UI mode for logins — Chrome for Developers · Passwordless sign-in with WebAuthn passkey autofill (conditional UI)

←Back to all posts
Next step

Need help shipping software?

Tell us what you're trying to build. A discovery call, a one-page summary within 48 hours, a proposal within a week.

Response · 48h·NDA on request·US contracts only