Inertia Rails TypeScript Setup
Type-safe shared props, flash, and errors using InertiaConfig module augmentation.
Works identically across React, Vue, and Svelte — the
globals.d.ts
and
InertiaConfig
setup is the same for all frameworks.
Before adding TypeScript types, ask:
Shared props (auth, flash)?
→ Update
SharedProps
/
FlashData
in
index.ts
— InertiaConfig in
globals.d.ts
propagates them globally via
usePage()
Page-specific props?
→
type Props = { ... }
in the page file only — never include shared props here
InertiaConfig Module Augmentation
Define shared props type ONCE globally — never in individual page components.
InertiaConfig property names are EXACT — do not rename them:
sharedPageProps
(NOT sharedProps)
flashDataType
(NOT flashProps, NOT flashData)
errorValueType
(NOT errorBag, NOT errorType)
// app/frontend/types/globals.d.ts
import
type
{
FlashData
,
SharedProps
}
from
'@/types'
declare
module
'@inertiajs/core'
{
export
interface
InertiaConfig
{
sharedPageProps
:
SharedProps
// EXACT name — auto-typed for usePage().props
flashDataType
:
FlashData
// EXACT name — auto-typed for usePage().flash
errorValueType
:
string
[
]
// EXACT name — errors are arrays of strings
}
}
// app/frontend/types/index.ts
export
interface
FlashData
{
notice
?
:
string
alert
?
:
string
}
export
interface
SharedProps
{
auth
:
{
user
?
:
{
id
:
number
;
name
:
string
;
email
:
string
}
}
}
Convention:
Use
auth: { user: ... }
as the shared props key — this matches the
Rails
inertia_share
community convention (
{ auth: { user: current_user } }
).
The
auth
namespace separates authentication data from page props, preventing
collisions when a page has its own
user
prop. Do NOT use
current_user:
or
user:
as top-level keys — they collide with page-specific props and break the
convention that other Inertia skills and examples assume.
BAD vs GOOD Patterns
// BAD — passing shared props as generics:
// usePage<{ users: User[], auth: AuthData, flash: FlashData }>()
// BAD — extending a SharedProps interface into page props:
// interface Props extends SharedData { users: User[] }
// BAD — declaring PageProps interface:
// interface PageProps { auth: AuthData; flash: FlashData }
// BAD — using current_user or user as top-level shared key:
// interface SharedProps { current_user: User }
// BAD — destructuring auth directly from usePage() (TS2339: 'auth' does not exist on Page):
// const { auth } = usePage()
// usePage() returns a Page object with { props, flash, component, url, ... }
// auth lives inside props, not on the Page itself
// BAD — duplicating InertiaConfig in index.ts (it belongs in globals.d.ts):
// declare module '@inertiajs/core' { ... } ← in index.ts
// GOOD — props from usePage().props, flash from usePage().flash:
const
{
props
,
flash
}
=
usePage
(
)
// props.auth is typed (from SharedProps via InertiaConfig)
// flash.notice is typed (from FlashData via InertiaConfig)
Important:
globals.d.ts
configures InertiaConfig ONCE. When adding a new shared
prop, only update
index.ts
— do NOT touch
globals.d.ts
:
// BEFORE — app/frontend/types/index.ts
export
interface
SharedProps
{
auth
:
{
user
?
:
{
id
:
number
;
name
:
string
;
email
:
string
}
}
}
// AFTER — add the new key here, NOT in globals.d.ts
export
interface
SharedProps
{
auth
:
{
user
?
:
{
id
:
number
;
name
:
string
;
email
:
string
}
}
notifications
:
{
unread_count
:
number
}
}
InertiaConfig in
globals.d.ts
references
SharedProps
by name — it picks up the
change automatically. Adding a second
declare module '@inertiajs/core'
causes conflicts.
Page-Specific Props
Page components type ONLY their own props. Shared props (like auth) and flash come from
InertiaConfig automatically.
type
vs
interface
for page props (React-specific)
This constraint applies to
React only
. Vue's
defineProps
Common TypeScript Errors
Error
Cause
Fix
TS2344
on
usePage