Skip to content

Repository files navigation

vite-plugin-ferry

Type-safe Inertia apps end to end: TypeScript for routes, enums, resources, page props, and form types, generated straight from your Laravel backend.

npm version npm downloads License

Ferry reads your Laravel app and generates TypeScript for the surface your Inertia frontend touches: named routes, PHP enums, JsonResource shapes, per-page Inertia props, and FormRequest data. Your frontend is typed from the backend that owns the data, so a change to a resource or a rule shows up as a type error where the frontend uses it. Nothing lands in your project tree: runtime code ships as Vite virtual modules, types as one generated ambient .d.ts, and everything regenerates on every run.

Install

npm install vite-plugin-ferry typescript@^5 --save-dev

Quick start

Add the plugin to vite.config.ts:

import { defineConfig } from 'vite';
import ferry from 'vite-plugin-ferry';

export default defineConfig({
  plugins: [ferry()],
});

That's it. No tsconfig changes, no generated files to gitignore. See Getting started for how generation runs and how the types load.

Ferry generates six virtual modules you import from directly:

import { OrderStatus } from '@ferry/enums';        // enum classes
import type { PostResource } from '@ferry/resources'; // resource shapes
import type { UsersShowProps } from '@ferry/pages';   // per-page Inertia props
import type { StoreUserRequest } from '@ferry/forms';  // form data shapes
// @ferry/route and @ferry/enum back route() and the Enum base class

The typed surface, in one page component:

import type { UsersShowProps } from '@ferry/pages';
import type { StoreUserRequest } from '@ferry/forms';

const page = usePage<UsersShowProps>();
page.props.user;             // UserResource

const href = route('users.show', { user: 1 }); // usable as a string AND as Inertia's { url, method }
route.is('users.*');                            // current-route check: name, wildcard, or an array of either

const form = useForm<StoreUserRequest>({ name: '', role: 'admin' });
form.data.role;              // 'admin' | 'editor' | 'viewer'
form.errors['profile.bio'];  // error keys derived from the shape

What it generates

  • Routes (@ferry/route): a typed route() helper that resolves named routes to their URL and method client-side, with zero route table shipped to the browser.
  • Enums (@ferry/enum, @ferry/enums): PHP enums become real JS classes with is/from/fromOrFail/values/keys/cases/options, plus a <Enum>Value backing-value union for serialized data.
  • Resources (@ferry/resources, @ferry/pagination): precise types for your JsonResource classes from static toArray() analysis plus real column and cast metadata, degrading gracefully instead of breaking your build. Resource::collection() over a paginator types as the real { data, links, meta } envelope.
  • Page props (@ferry/pages): the props each Inertia page receives, typed through usePage<T>(), with shared props typed through Inertia's own augmentation.
  • Form types (@ferry/forms): the data shape of your FormRequest classes, typed through useForm<T>() with form.errors keys derived for free.
  • Environment variables (import.meta.env): every VITE_-prefixed env var typed as string, merged into Vite's own ImportMetaEnv — keys only, no values, no import.

Any field ferry can't resolve statically degrades instead of breaking the build, and you can pin it precisely with a @ferry docblock tag.

Documentation

Contributing

Issues and pull requests are welcome. Run the test suite with npm test.

License

See LICENSE for details.

About

Type-safe Inertia apps end to end — generates TypeScript for routes, enums, resources, page props, and form types straight from your Laravel backend.

Topics

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages