Rails + Inertia.js + Svelte
Source:
en/integrations/inertia-svelte.md· Live: https://docs.1pass.dev/en/integrations/inertia-svelte LLM-sanitized: internal links absolutized, VitePress containers → admonitions, line numbers in the Jump-to Index reference this rendered file (1-indexed).
📍 Jump-to Index
- L34-L52: ## ① Starting login — bypass the client-side router (most important)
- L53-L86: ## ② Login state — share current_user via
inertia_share - L87-L108: ## ③ In Svelte —
pageis a rune, not a store (page.props) - L109-L115: ## Checklist
- L116-L121: ## See also
Rails + Inertia.js + Svelte
ℹ️ Note: Read this first This page is a companion to the Rails 8 guide. All server-side logic — controller, PKCE, token exchange, JWT verification — is the same as in that guide. This page covers only the three differences for an Inertia.js + Svelte frontend.
⚠️ Warning: 📋 Register a Confidential client The Rails server sends
client_secretto/oauth/token, so register the app as Client type: Confidential. Never expose the secret to the Svelte frontend.
In an Inertia app, page transitions are intercepted by a client-side router (@inertiajs/svelte's router, use:inertia, <Link>). 1pass login starts with a cross-origin 302 redirect — and a client-side router tries to follow that 302 over XHR and fails silently (the same failure mode as Turbo Drive). So three things change.
① Starting login — bypass the client-side router (most important)
The "Log in with 1pass" button must be a full-page navigation. Do not use use:inertia / <Link> / router.visit.
<a href="/auth/logi/start" data-no-inertia>Log in with 1pass</a>Choose the /auth/logi/start route in your app (see Login Button). The controller's redirect_to(..., allow_other_host: true) is the same as in the Rails guide.
💡 Tip: The callback does not need special handling
/auth/logi/callbackis handled by the server controller and finishes withredirect_to root_path, so Inertia renders the next page as usual. Only the start (①) needs the bypass, not the callback.
② Login state — share current_user via inertia_share
To expose the logged-in user on every Inertia page without an extra round-trip, use ApplicationController#inertia_share.
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
inertia_share do
{
auth: {
user: current_user && {
id: current_user.id,
email: current_user.email,
displayName: current_user.display_name
}
},
flash: { notice: flash[:notice], alert: flash[:alert] }
}
end
private
def current_user
@current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
end⚠️ Warning: Don't create users from front-channel data
current_usermust come only from the server session (session[:user_id]). Create users only from the server-sideuserinforesponse — see Rails guide: first login.
③ In Svelte — page is a rune, not a store (page.props)
🚨 Danger: @inertiajs/svelte v3 trap The v2
$pagestore pattern breaks in v3. In v3,pageis a$staterune object, so subscribing with$pagefails hydration withstore_invalid_shape. Readpage.propsdirectly.
<header>
{#if user}
<span title={user.email}>👤 {user.displayName}</span>
<button type="button" onclick={logout}>Log out</button>
{:else}
<a href="/auth/logi/start" data-no-inertia>Log in with 1pass</a>
{/if}
</header>Logout is safe via router.delete (a same-origin DELETE — not a cross-origin 302, so the client router can handle it).
Checklist
- [ ] Login start button is a plain
<a href>(notuse:inertia/<Link>) - [ ]
inertia_shareexposesauth.user;current_useris session-based - [ ] Svelte reads
page.propsdirectly, not$page - [ ] RP registered as Confidential; secret lives only in server env
See also
- Rails 8 — controller, PKCE, token exchange, JWT verification (all server-side)
- SPA + Serverless — pure SPA with no backend (Public client)
- Login Button — button design and start-route conventions