npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@inlayphp/resources-react

v0.3.11

Published

React renderer for Inlay resources and relation managers.

Readme

@inlayphp/resources-react

npm License

React renderer for Inlay resources and relation managers

React components for owner-scoped Inlay Relation Managers. The package contains no Laravel business rules: it renders the serialized PHP contract by composing @inlayphp/tables-react and @inlayphp/forms-react.

Install

pnpm add @inlayphp/resources @inlayphp/resources-react

Render registered managers

import { router } from '@inertiajs/react'
import {
  RelationManagers,
  type RelationManagerResource,
} from '@inlayphp/resources-react'

export function Relations({
  relations,
}: {
  relations: RelationManagerResource[]
}) {
  return <RelationManagers
    resources={relations}
    onChanged={() => router.reload({ only: ['relations'] })}
  />
}

Use RelationManager when a page renders one manager. Both components accept:

  • onChanged(operation, record) after create, edit, delete, restore, force-delete, attach, detach, associate, or dissociate.
  • onQueryChange(query) for server-side Table navigation.
  • empty to replace the default empty Table.
  • mutate(request) to replace the built-in fetch transport.
  • className for page-level layout without overriding component internals.

The built-in transport sends same-origin JSON with Laravel CSRF and XMLHttpRequest headers. Validation responses are mapped to the standard Form error contract. Create, edit, attach, and associate use an accessible modal with close, backdrop, and Escape behavior. Attach and associate dialogs render PHP-provided remote searchable Selects and resolve chosen identifiers into record-specific endpoints. Additional Attach fields are submitted as validated pivot data without any React-specific business logic. Edit dialogs use the dedicated PHP editForm contract, including hydrated withPivot() attributes, even when creation is not authorized. Delete, detach, and dissociate use the Table action confirmation flow.

When PHP registers managers inside RelationGroup::make(), RelationManagers automatically renders the group as accessible tabs. The configured default tab is selected first, and Arrow Left/Right plus Home/End move both selection and keyboard focus. No React group definition is needed.

The package deliberately does not cache relation rows after a mutation. Reload the relations Inertia prop so authorization, computed columns, filters, and pagination remain server-authoritative.

Compose a resource page

Use ResourcePage when a custom page wants the same familiar resource chrome without reimplementing breadcrumbs, record navigation, or named views. The server still owns the metadata; the page owns the actual Form, Table, Infolist, and RelationManagers content.

import { ResourcePage } from '@inlayphp/resources-react'
import { Form } from '@inlayphp/forms-react'

export function EditUser({ form, breadcrumbs, heading, subheading, subNavigation, errors }) {
  return (
    <ResourcePage
      actions={<button type="button">Delete</button>}
      breadcrumbs={breadcrumbs}
      heading={heading}
      subheading={subheading}
      subNavigation={subNavigation}
    >
      <Form errors={errors} resource={form} />
    </ResourcePage>
  )
}

Page widgets

ResourcePage consumes the headerWidgets and footerWidgets props published by PHP ResourcePage. Both props use the same inlay.widget-dashboard.v1 contract as the panel dashboard and are rendered by @inlayphp/widgets-react, so stats, charts, nested tables, lazy loading, polling, and custom renderers behave the same on a resource page and a dashboard.

import { ResourcePage } from '@inlayphp/resources-react'
import type { WidgetDashboardResource } from '@inlayphp/widgets-react'

export function ListOrders({
  footerWidgets,
  headerWidgets,
  ...props
}: {
  headerWidgets?: WidgetDashboardResource | null
  footerWidgets?: WidgetDashboardResource | null
  // ...the remaining ResourcePage props
}) {
  return (
    <ResourcePage
      {...props}
      footerWidgets={footerWidgets}
      headerWidgets={headerWidgets}
      widgetProps={{
        theme: { accent: '#4f46e5' },
        classNames: { grid: 'gap-8' },
      }}
    >
      {/* Table, Form, Infolist, or RelationManagers */}
    </ResourcePage>
  )
}

widgetProps is forwarded to both dashboards. Use it for theme tokens, custom icons/renderers, an empty state, or an onRefresh callback. The page keeps widget placement server-authored: header dashboards render before beforeContent and the page content, while footer dashboards render after afterContent. Omitted or null widget props render no dashboard region.

PHP page actions are available as the headerActions prop. They are rendered by @inlayphp/actions-react, so confirmation dialogs, dynamic action forms, validation errors, keyboard bindings, and focus return use the same runtime as table and form actions. The default executor visits ordinary action URLs with Inertia and sends lifecycle actions to the JSON action endpoint:

import type { ActionResource } from '@inlayphp/actions'
import { ResourcePage } from '@inlayphp/resources-react'

export function EditUser({
  headerActions,
  recordId,
  ...props
}: {
  headerActions?: ActionResource[]
  recordId: number
  // ...the remaining server props
}) {
  return (
    <ResourcePage
      {...props}
      headerActions={headerActions}
      actionInput={{ parameters: { id: recordId } }}
    >
      {/* Form, Table, Infolist, or RelationManagers */}
    </ResourcePage>
  )
}

actionExecutor replaces that transport when an application uses a custom request client. actionInput is merged into each action invocation and is useful for page-level parameters or records. The existing actions prop remains an ordinary ReactNode slot; when both props are supplied, custom slot content and server-declared actions are rendered together.

tabs accepts the PHP ListRecords named-view contract. Handle onTabSelect by visiting the same page with the selected tab; the component deliberately does not filter records in the browser.