netresearch/assetpicker
Netresearch AssetPicker is a TYPO3 CMS extension that adds a backend asset picker for selecting files from the File Abstraction Layer. Editors can browse and choose assets for TCA fields via a wizard, making file selection in records and content simpler and more consistent.
Install the frontend package alongside your Laravel app's existing toolchain:
npm install netresearch/assetpicker
It is a Vue 3 component, not a Laravel package — there is no service provider or Composer entry. Treat it as a UI dependency of your Vite build.
Read the adapter contract first. Before writing any integration code, open the package's adapter interface (usually src/adapters/ and an AdapterInterface or similar type definition). Every workflow below hinges on it: the picker UI is generic, and all provider behavior (listing, searching, uploading, URL resolution) is delegated to an adapter. If the README is thin, the shipped GitHub/Google Drive adapters are your best documentation — they are working reference implementations.
Start with the bundled GitHub adapter in a throwaway route. Mount the picker on a plain Blade page with a hardcoded test repo. This validates your Vite config, component import, CSS, and event wiring before you introduce auth complexity:
<div id="assetpicker"></div>
@vite(['resources/js/assetpicker.js'])
First real use case: a media/image field in an admin form. Replace a plain <input type="file"> with the picker so editors browse a Drive folder or GitHub repo and select an existing asset. The picker emits a selection event; you store the returned URL/path string in a normal Eloquent column. This is the lowest-risk, highest-value integration and doesn't require upload support.
Token brokering through Laravel controllers. Never ship provider credentials in your JS bundle. The picker's adapters that hit remote APIs need tokens; issue short-lived, scoped tokens from a Laravel endpoint and pass them into the component as props. For GitHub, a fine-grained PAT or a GitHub App installation token minted server-side. For Google Drive, this is non-negotiable — you need an OAuth flow, and the standard shape is: Laravel handles the redirect/callback, stores the refresh token encrypted per user, and exposes a GET /api/drive-token route that returns a fresh access token to the frontend just before mounting.
Write a thin custom adapter rather than fighting a bundled one. The pluggable layer exists precisely so you can wrap your own storage. A typical Laravel-side adapter implements list, search, and getUrl against your own /api/assets endpoints, returning the package's normalized file shape. Benefits: authorization lives in Laravel policies, you get pagination and filtering for free via Eloquent, and you're not coupling the picker to a third-party API's quirks. Use the bundled providers only when the assets genuinely live in those systems.
Mounting in Inertia vs. Blade. In Inertia, render the picker inside a page component and pass adapter config through page props — this is the cleanest path and keeps state in Vue. In Blade, mount with createApp on a dedicated element and pass config via a data- attribute containing @json($config); keep the mounting script tiny and isolated so it doesn't collide with any other Vue instance on the page.
Handle selection as a two-step: pick, then persist. Treat the picker as returning a descriptor ({ id, url, name, mime, size }), not a finished upload. On the select callback, POST that descriptor to a Laravel endpoint that validates it (allowlist of hosts/MIME types) and writes your assets record. This keeps the picker stateless and makes the same component reusable for "attach existing" and "replace asset" flows.
Multi-tenant config injection. Because config is just props, you can vary it per user or team: each tenant's Drive folder ID, each repo's branch, or a different adapter entirely. Resolve this in a Laravel controller and hand it down, rather than branching inside Vue.
Testing without the network. Build a stub adapter backed by an in-memory array of fixtures and register it in your test environment. You get deterministic component tests (Vitest/Cypress) and can exercise selection and error states without OAuth tokens or rate limits.
localhost:5173 or your app domain need the right origins and, for Drive, the right OAuth consent scopes. If listings come back empty, check the Network tab for preflight failures before suspecting the adapter.<img src> breaks for end users, you're storing a protected URL. Download through a Laravel proxy route and store your own stable path instead.search maps to a Laravel endpoint, the picker may call it on every keystroke. Guard with a debounce and cancel in-flight requests, or add a minimum-length check server-side.console.log at construction; (2) are requests leaving the browser at all (Network tab) or failing before dispatch; (3) is the response shape normalized to what the picker expects — a 200 with an unexpected array shape renders as an empty list with no error; (4) is the component emitting select before your listener is attached (mount race in Blade). Add an explicit error/empty state to the picker wrapper so adapter failures surface instead of looking like "no files."@vite entry imports them and that Tailwind (if used) isn't purging classes inside node_modules — configure content to include the package path or you'll get an unstyled grid.How can I help you explore Laravel packages today?