Google Search Console - v1.0.0
React Components
Three React components ship as source under resources/js/react/. They fetch client-side from the HTTP endpoints and render the same reports as the Livewire and Vue equivalents.
Requirements
- React 18+ (uses
useEffect,useState,useCallback). - A bundler that compiles TSX (Vite, Webpack, Turbopack, …).
- An authenticated session — the endpoints run under
web+authmiddleware, so the browser must be sending the app's session cookie with the fetch.
Getting the sources
Import directly from the package (if your bundler can resolve into vendor/):
import { PerformanceCard } from '@artisanpack-ui/google-search-console/react/PerformanceCard'
import { TopQueriesTable } from '@artisanpack-ui/google-search-console/react/TopQueriesTable'
import { TopPagesTable } from '@artisanpack-ui/google-search-console/react/TopPagesTable'
Or publish the sources into your app tree:
php artisan vendor:publish --tag=google-search-console-js
Copies resources/js/react/, resources/js/vue/, and resources/js/shared/ under resources/js/vendor/google-search-console/. From there:
import { PerformanceCard } from '@/vendor/google-search-console/react/PerformanceCard'
<PerformanceCard />
Four-metric summary + daily trend.
<PerformanceCard initialDays={28} />
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
initialDays |
number |
28 |
Starting range. The component's built-in range dropdown lets the user switch to 7 / 28 / 90 / 180. |
siteUrl |
string | null |
null |
Override the configured site. Ignored by the server — the controller reads config only. Useful only if you're pointing baseUrl at your own endpoint. |
baseUrl |
string |
/google-search-console |
Prefix for the JSON endpoint. Change if your app moved the routes via google-search-console.routes.prefix. |
fetchImpl |
typeof fetch |
window.fetch |
Injectable fetch — swap for SSR or testing. |
State transitions
The component internally tracks:
data: GscPerformanceData | null— populated on first successful fetch.loading: boolean— mid-flight indicator.errorCode: string | null— one ofunauthenticated,not_connected,base_not_installed,reporting_error, ornull.errorMessage: string | null— server-provided message for the current error.
Renders one of:
- Missing base (
errorCode === 'base_not_installed') — install-the-base CTA. - Not connected (
errorCode === 'not_connected') — connect-a-Google-account CTA. - Unauthenticated (
errorCode === 'unauthenticated') — sign-in CTA. - Reporting error (
errorCode === 'reporting_error') — the message from Google. - No data —
data.hasData === false. - Success — totals tiles + daily-clicks bar trend.
<TopQueriesTable />
Sortable, paginated table of top queries.
<TopQueriesTable initialDays={28} initialLimit={50} />
Props
| Prop | Type | Default | Notes |
|---|---|---|---|
initialDays |
number |
28 |
Starting range. |
initialLimit |
number |
50 |
Number of rows to pull from Google in one request. Clamped server-side to 1..1000. |
siteUrl |
string | null |
null |
(See caveat under PerformanceCard.) |
baseUrl |
string |
/google-search-console |
|
fetchImpl |
typeof fetch |
window.fetch |
<TopPagesTable />
Same API as TopQueriesTable, but rows are keyed by page URL instead of query text.
<TopPagesTable initialDays={28} initialLimit={50} />
The shared fetch client
Under the hood every component calls one of three functions in resources/js/shared/:
fetchGscPerformance(options): Promise<GscPerformanceData>fetchGscTopQueries(options): Promise<GscTopQueriesData>fetchGscTopPages(options): Promise<GscTopPagesData>
They handle URL construction, JSON parsing, and wrap every non-2xx response in a typed error class (GscPerformanceError, etc.) with a code matching the server's errorCode.
Call them directly if you want your own UI:
import { fetchGscPerformance, GscPerformanceError } from '@artisanpack-ui/google-search-console/shared/performance'
async function load() {
try {
const data = await fetchGscPerformance({ days: 28 })
// data.totals, data.trend, data.hasData, data.range
} catch ( err ) {
if ( err instanceof GscPerformanceError ) {
// err.code, err.message
}
}
}
See Components/Custom for the full API.
Aborting in-flight requests
Every component wraps its fetches in an AbortController that's aborted on unmount and when a new range is selected — no zombie requests, no state-after-unmount warnings. If you're calling fetchGscPerformance directly, pass a signal:
const controller = new AbortController()
fetchGscPerformance({ days: 28, signal: controller.signal })
// later:
controller.abort()
Styling
The components emit BEM-classed markup (ap-gsc-performance__tile, ap-gsc-top-queries__sort, …) with no styles applied. Bring your own CSS. If you're on Tailwind, wrap the component in a container and use Tailwind's [&_.ap-gsc-performance__tile]:... variant syntax to style descendants without touching the source.
Testing
Use React Testing Library with a fake fetch:
import { render, screen } from '@testing-library/react'
import { PerformanceCard } from '@artisanpack-ui/google-search-console/react/PerformanceCard'
const fakeFetch = async ( input: RequestInfo | URL ) => new Response(
JSON.stringify( {
range: { startDate: '2026-01-01', endDate: '2026-01-28' },
totals: { clicks: 42, impressions: 500, ctr: 0.084, position: 3.2 },
trend: [],
hasData: true,
} ),
{ status: 200, headers: { 'Content-Type': 'application/json' } },
)
test( 'renders totals from fake fetch', async () => {
render( <PerformanceCard fetchImpl={fakeFetch as typeof fetch} /> )
expect( await screen.findByText( '42' ) ).toBeInTheDocument()
} )
The fetchImpl prop is deliberately typed so the injected function can differ from real fetch — the components only call fetchImpl(url).
Publishing sources into your app
php artisan vendor:publish --tag=google-search-console-js
Copies:
resources/js/vendor/google-search-console/react/— three.tsxfiles.resources/js/vendor/google-search-console/vue/— three.vuefiles.resources/js/vendor/google-search-console/shared/— the fetch client.
From there you can edit freely. git-ignore the vendor path or check it in, depending on your policy.