inertia-rails-typescript

TypeScript type safety for Inertia Rails (React, Vue, Svelte): shared props, flash, and errors via InertiaConfig module augmentation in globals.d.ts. Use when setting up TypeScript types, configuring shared props typing, fixing TS2344 or TS2339 errors in Inertia components, or adding new shared data.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "inertia-rails-typescript" with this command: npx skills add inertia-rails/skills/inertia-rails-skills-inertia-rails-typescript

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<T>() and Svelte's $props() do not use usePage<T>() generics, so interface works fine there.

usePage<T>() requires T to have an index signature. type aliases have one implicitly; interface declarations do not. Using interface with usePage causes TS2344 at compile time.

PatternWorks with usePage<T>()?Notes
type Props = { users: User[] }YesPreferred — just works
interface Props { users: User[] }No — TS2344Missing index signature
usePage<Required<Props>>()YesWraps interface to add index signature
// React
type Props = {
  users: User[]         // page-specific only
  // auth is NOT here — it comes from InertiaConfig globally
}

export default function Index({ users }: Props) {
  // Access shared props separately:
  const { props, flash } = usePage()
  // props.auth is typed via InertiaConfig
  // flash.notice is typed via InertiaConfig
  return <UserList users={users} />
}

Accessing shared props in Vue and Svelte

Vue and Svelte use different patterns to access shared props, but InertiaConfig typing works the same way.

<!-- Vue 3 — usePage() returns reactive object; use computed() for derived values -->
<script setup lang="ts">
import { usePage } from '@inertiajs/vue3'
import { computed } from 'vue'

const page = usePage()
const userName = computed(() => page.props.auth.user?.name) // typed via InertiaConfig
</script>
<!-- Svelte — page store from @inertiajs/svelte -->
<script lang="ts">
  import { page } from '@inertiajs/svelte'
  // $page.props.auth is typed via InertiaConfig
  // $page.flash.notice is typed via InertiaConfig
</script>

Common TypeScript Errors

ErrorCauseFix
TS2344 on usePage<Props>()interface lacks index signatureUse type Props = { ... } instead of interface, or wrap: usePage<Required<Props>>()
TS2339 'auth' does not exist on type PageDestructuring auth from usePage() directlyusePage() returns { props, flash, ... } — use usePage().props.auth, not usePage().auth
TS2339 'flash' does not exist on typeAccessing usePage().props.flashFlash is top-level: usePage().flash, NOT usePage().props.flash
Shared props untypedMissing InertiaConfigAdd globals.d.ts with module augmentation (see above)
InertiaConfig not taking effectDeclaration in wrong fileMust be in a .d.ts file (e.g., globals.d.ts), not in .ts — TypeScript ignores declare module in regular .ts files that have imports/exports
Types correct but IDE shows errorsglobals.d.ts not includedVerify tsconfig.app.json includes the types directory in include array

Typelizer Integration

If using the typelizer gem (see alba-inertia skill), SharedProps are auto-generated from your serializer — do NOT manually write the SharedProps interface in index.ts. You only write globals.d.ts once (the InertiaConfig augmentation). When you add a new attribute to SharedPropsResource, Typelizer regenerates index.ts and the types propagate via InertiaConfig — no manual type updates needed.

Related Skills

  • Shared props setupinertia-rails-controllers (inertia_share)
  • Flash configinertia-rails-controllers (flash_keys)
  • Auto-generated typesalba-inertia (Typelizer + Alba resources)
  • Page component propsinertia-rails-pages (type Props pattern)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

General

inertia-rails-architecture

No summary provided by upstream source.

Repository SourceNeeds Review
General

inertia-rails-controllers

No summary provided by upstream source.

Repository SourceNeeds Review
General

inertia-rails-pages

No summary provided by upstream source.

Repository SourceNeeds Review
General

inertia-rails-forms

No summary provided by upstream source.

Repository SourceNeeds Review