[@my-eyes](https://github.com/my-eyes)/react — the fourth renderer, and the last one on the roadmap.
Every component [@my-eyes](https://github.com/my-eyes)/vue exports, under the same names: the primitives,
the form controls, the overlays, the admin shell, the server-driven table and
all twelve authentication screens. React 19, function components, no runtime
dependency of its own.renderCell), a named slot is a node prop (actions, footer,
aside), an emit is a callback prop (onSubmit), and v-model is value
plus onValueChange. onValueChange hands over the parsed value rather than
the event, so the native onChange still reaches the element.'use client' on everything that touches the DOM, so the package can be
imported from a React Server Components build without breaking it.[@my-eyes](https://github.com/my-eyes)/core, and the hook reads it through
useSyncExternalStore, so the double mount applies exactly one result.RendererParityTest now counts three client renderers against Blade, and
compares React's exports against Vue's directly — a name that drifted in both
at once would have satisfied the old counts.packages/core/tests/fixtures.ts and is
shared: a new test renders the Vue and the React table from the same payload
and compares the markup they produce. Two copies of a fixture cannot prove
parity.:split="false"
restores the single centred column exactly.clamp() scale. A centred heading over left-aligned fields was the detail
that made the screen read as a greeting card.layouts.auth and MeAuthLayout take split, image, tagline and
reverse, plus an aside slot for the visual half. Every full-page screen
forwards all of them.--color-primary-* and
--color-info-*, so remapping the roles moves it too — and it costs no bytes.
A photograph is an <img> with alt="" and a scrim, never a
background-image: decorative, unannounced, and no URL interpolated into CSS.The authentication screens now exist for Vue, not only Blade: MeLoginScreen,
MeRegisterScreen, MeForgotPasswordScreen, MeResetPasswordScreen,
MeConfirmPasswordScreen, MeVerifyEmailScreen and
MeTwoFactorChallengeScreen, plus a card per profile section. A screen emits
its payload and the application makes the request — the same rule the table
already followed, so the package still knows nothing about Inertia, CSRF or
routing.
Two-factor authentication, in both renderers, against Fortify's endpoints. The
card renders exactly one of Fortify's three states, and the challenge screen
sends code or recovery_code and never both — sending both is what produces
the failure that is hard to read.
Passkeys, in both renderers. The WebAuthn ceremony lives once in
[@my-eyes](https://github.com/my-eyes)/core — registerPasskey(), authenticateWithPasskey(),
confirmWithPasskey() — and Blade drives it through data-me-passkey. No
WebAuthn library was added: browsers convert the JSON themselves, and the
fallback for those that do not is 80 lines and directly tested.
A passkey affordance is never offered where it cannot work. Both renderers feature-detect, and a dismissed browser prompt leaves the screen untouched rather than reporting an error.
The profile screen takes an avatar: a plain file field named avatar, with
the initials fallback the avatar component already had. Storage stays the
application's.
RendererParityTest now counts the screens too, so a Blade page and its Vue
screen cannot drift apart any more than the components can.
96 more icons — 128 in all — drawn for CRUD, ERP and CRM work: files, tables, commerce, people, charts, notifications and system. One family: 24×24 grid, 1.75 stroke, round terminals.
The set now has one source, resources/icons/*.svg, generated into PHP and
TypeScript by bin/build-icons.php. It used to be written out twice with a
test holding the two in step; the generator removes the class of bug that
test existed to catch, and adding an icon is now dropping in an SVG.
registerIcons() in [@my-eyes](https://github.com/my-eyes)/core and an icons key in
config/my-eyes.php. An icon set is a styling decision, and this package's
rule is that those belong to the application — so bring Font Awesome, or any
other set, without the package depending on one.
bin/render-icons.php renders a contact sheet at reading size and at 16px,
and docs/icon-authoring.md documents the loop. Detail that survives one
size and vanishes in the other is the most common mistake in an icon set.
The showcase has an icon section.
MeIcon rendered an empty <svg> for a name it did not know — a typo became
an invisible button rather than an error. TypeScript cannot catch it, because
the registry is open by design and the name is often computed, so it now
warns once per unknown name. Blade throws while app.debug is on.<svg> wrapper by hand, and that copy stopped matching the day the
design system changed it. Both now take the geometry as a slot, wrapper
included.MeAuthLayout and MeErrorLayout, the last two Blade components without a
Vue equivalent. Like MeAdminLayout they render body content rather than a
document.MePagination takes hrefFor, turning its items into real links so
middle-click and "open in new tab" work. A plain click still fetches without
reloading, and a modified click is left to the browser. Without it the items
stay buttons, which is right when the pages have no addresses.as on every Vue component that renders a link — MeNavItem,
MeNavSubitem, MeDropdownItem, MeBrand and MeButton with an href.
It defaults to 'a', so nothing changes for Blade or for a Vue application
without a router; pass Inertia's Link or RouterLink to keep navigation
client-side. The package does not detect the router in use — that would tie
it to one and break everyone on another.startMyEyes() re-binds after inertia:navigate, alongside the Livewire and
Turbo hooks it already had. An Inertia page arrived unbound before, and every
application had to call initMyEyes() itself.MeModal takes v-model:open and emits close, so a dialog can be driven
from state instead of only from data-me-modal-open. Dismissal is reported
however it happened, so a parent's boolean cannot be left stuck on true.MeAlert takes v-model:visible and emits dismiss.MeDropdown emits update:open.useTheme() — the colour scheme as reactive state, for a chart or a canvas
that needs its own palette.openModal is exported from [@my-eyes](https://github.com/my-eyes)/core.MeAlert carried data-me-dismiss, whose binding removes the
element from the document. That element belongs to Vue, and tearing it out
from underneath corrupts the next patch. Vue now owns the dismissal.[@my-eyes](https://github.com/my-eyes)/core, the same code Blade and Livewire
run.[@my-eyes](https://github.com/my-eyes)/core's icons), with a test keeping
it in step with the Blade component.[@my-eyes](https://github.com/my-eyes)/core, so a component
framework can bind the element it just mounted instead of scanning the page.MeField emitted me-field__label, me-field__hint and me-field__error,
none of which exist in the stylesheet — labels, hints and errors rendered
unstyled. It now emits me-label, me-hint and me-error, as Blade does.MyEyes\Livewire\TableComponent and
MyEyes\Livewire\InteractsWithTable, sharing the query building and the
query string keys with the Blade table.Table::toPayload() and Jsonable, serialising a table for clients that
render rows from data — the contract the Vue and React renderers will speak.Column::html(), opting a column into sending markup in that payload.[@my-eyes](https://github.com/my-eyes)/vue — the table for Vue 3, plus the primitives it leans on. Server
pagination as everywhere else, with pages already fetched served from memory
and rows outside the viewport kept out of the DOM.[@my-eyes](https://github.com/my-eyes)/core, shared by every
renderer that fetches rather than receives markup.docs/, written before the renderers it describes,
including a reference for LLMs at docs/llm-reference.md.primary, secondary, success,
danger, warning, info), light and dark declared once through
light-dark().How can I help you explore Laravel packages today?