A static React + Tailwind web app that renders Google-like search results pages from JSON configs for controlled experiments. No Google logos or copyrighted assets are used.
- AI Overview box (toggleable via JSON)
- Organic results and ad-styled results (clearly labeled "Ad")
- About ~10 results per page
- Click-through opens target in a new tab
- Experimenter controls: pick query, next/prev, randomize/sequential order, download CSV log
- In-memory click logging with CSV export (timestamp, sessionId, query, url)
- Config-driven content; add/modify JSON without code changes
- Service worker for basic offline use after first load
- React 18, Vite
- Tailwind CSS 3
- Install dependencies
npm install- Run dev server
npm run devOpen the shown localhost URL in your browser.
- Build for static hosting
npm run build
npm run previewYou can host dist/ on any static host. The app runs offline after first load thanks to a simple service worker.
index.html– Vite entrysrc/– React codeApp.jsx– experimenter header/controls, config selection, loggingpages/SearchPage.jsx– page layout for a single querycomponents/AIOverview.jsx– AI summary cardcomponents/SearchResult.jsx– organic resultcomponents/AdResult.jsx– ad-styled resultutils/configLoader.js– dynamic fetch of JSON configsutils/logger.js– in-memory logger + CSV exportindex.css– Tailwind directives and SERP-like styles
public/configs/– JSON config files (loaded at runtime)public/sw.js– simple offline cache
Configs live under public/configs/. At minimum you need:
public/configs/index.json– array of objects withlabelandpathfields, e.g.
[
{ "label": "Best hiking boots", "path": "/configs/query1.json" },
{ "label": "Healthy breakfast ideas", "path": "/configs/query2.json" }
]- One JSON file per query (e.g.,
public/configs/query1.json) with shape:
{
"query": "best hiking boots",
"aiOverview": { "show": true, "text": "AI summary here" },
"results": [
{ "title": "Result title", "url": "https://example.com", "snippet": "Short description...", "ad": false },
{ "title": "Shop ...", "url": "https://store.example/", "snippet": "...", "ad": true }
]
}Notes:
- Toggle AI Overview by setting
aiOverview.showtrue/false; text is sourced fromaiOverview.text. - Control order simply by ordering the
resultsarray; includead: trueto render with ad styling. - The UI renders the first ~10 results per page.
- Every outbound click is recorded in-memory with
{ timestamp, sessionId, query, url }. - Use the "Download CSV" button in the header to save the current session as a CSV.
- Optional backend: You can later POST
logger.eventsto a Node.js endpoint to persist logs.
After first load, the service worker caches core assets and the config index so the app can run offline. If you add new configs, users should revisit once online to refresh the cache.
Tested with latest Chrome, Firefox, and Edge. For best fidelity, ensure fonts are available; the app uses a system sans-serif stack.
For research use only. No Google assets are included.