# Modular Grid Page: a React start page you configure in the browser

> Modular Grid Page is a browser start page built as a React app with a responsive tile grid, a settings panel that persists to IndexedDB, and tiles for photos, bookmarks, weather, a solar arc, a clock, a feature panel and a multi-engine search box. It suits someone who wants a new tab page they can rearrange without editing files, and the detail to understand first is that it ships in two forms, a local-only build and a hosted one with accounts and sync.

**timh-dev/startup-page** — A custom startup page for your browser. 

- Repository: https://github.com/timh-dev/startup-page
- Website: https://box-start-page.vercel.app
- Stars: 527 · Forks: 51
- Language: TypeScript
- License: MIT
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/timh-dev-startup-page

## The idea, and the layout

Modular Grid Page is a fast, customizable browser homepage built as a React app, with the layout being a responsive auto-sizing tile grid. Every tile, theme and data source is configurable through the built-in settings panel, with no config files to edit by hand.

That last clause is the whole positioning. A start page is a small application whose configuration is usually a JSON file, which means every change means opening an editor, and for a page you look at several times a day that is friction you feel. This one puts the configuration behind a gear icon on the dashboard.

The grid itself adjusts its column count to the viewport width, and the tile size is adjustable through a slider in Settings, Layout, from 7 to 14 rem. So the density is a user choice on a continuum rather than a preset, and the rem unit is the right one for it since it scales with the browser's font size.

Tiles can also be hidden individually from the same panel without losing their position, which is a detail that sounds trivial and is not. The alternative, removing a tile entirely, means that when you want it back you have to reconstruct its configuration, and the common case is that you want a tile back after a week of not needing it.

There is a command palette too, reachable with Cmd+K or Ctrl+K, which navigates the dashboard by keyboard and is powered by kbar. That is a sensible addition to a page whose entire purpose is launching things, since a keyboard-driven way to open a bookmark is faster than finding the tile and clicking it.

Getting it running is three commands, and the Makefile does the work:

```sh
git clone https://github.com/timothypholmes/startup-page.git
cd startup-page
make setup
```

`make setup` installs dependencies and produces a production build, and then `make dev` starts the dev server:

```sh
make dev
```

The project is MIT licensed, the primary language is TypeScript, the default branch is main, and the last push was on 2026-09-11. There is one release, 1.0, tagged as the Start-page MVP in January 2023, and the package manifest is at version 0.1.0, which is worth noting because it means the published version and the internal version have drifted apart.

The demo site is at timothypholmes.github.io/startup-page and the homepage for the project is box-start-page.vercel.app, so there is a static demo and a hosted instance.

## The tiles

The tile list is where the personality of the page lives, and it is a long one.

There are six photo tiles, pulling from Unsplash or Wikimedia, one per configurable topic list, with the images cached locally through the Cache API and localStorage for instant display on return visits. The caching is the part that makes a photo tile bearable on a start page, since a start page is opened many times a day and a network request for a photo each time would be a waste.

Five bookmark tiles hold editable link groups with a title, a name and a URL per entry. The weather tile shows current conditions and temperature through the OpenWeather API. The clock tile shows live local time.

The solar graph is the most unusual of the set. It is a canvas-drawn arc showing today's sun position, twilight events, and a hover scrubber. So it is not a static picture of the sun's path; it is drawn from the day's data, and you can move the cursor along the arc to see what the sky was doing at a given moment. That is a start page widget doing something a photograph could not.

The feature panel cycles between Headlines from Reddit, a Windy map, a Pomodoro timer and a photo tile, with arrow buttons or dots to switch modes. A cycling panel is a common start page idiom and it works, though it also means the tile is showing four different things over time rather than one.

There are two decorative video tiles, a looping background video, described as a tall and a small viewport into the same shared scene. That detail is worth noting: it is one video rendered twice at different sizes rather than two separate videos, so the two tiles stay in sync and cost one fetch.

The search box is multi-engine, which is the tile most people will use most.

The set is deliberately mixed between functional and decorative. The clock, the search box and the bookmarks are tools, the photos and the video are decoration, and the solar graph and the feature panel sit between the two. The tiles are all on one grid with one set of appearance controls, so the mixture is what you get and the settings panel has a decorative media toggle for the video rather than a per-tile configuration for everything.

## Appearance, and the settings panel

Appearance gets a long feature list of its own, and it is the most unusual part of the project.

There are several built-in theme palettes plus a custom theme editor where you paste shadcn-compatible CSS variables. Shadcn-compatible is a meaningful constraint, because the components are shadcn-style, so a theme that follows the same variable naming applies across the whole interface without any per-component work.

Then light, dark and system modes, which is the expected set and worth having because system mode follows the operating system.

Card shape is a choice of soft, rounded or sharp corners, which is a small thing that changes how a page feels more than it should.

The image effects are the part to slow down on. Per-photo-tile GPU filters are available: Paper Texture, Fluted Glass, Water, Image Dithering, Halftone Dots and Halftone CMYK, via Paper Design shaders. These are WebGL shader effects applied to the photos, which is a considerably more demanding thing than a CSS filter, and the fluted glass and halftone CMYK effects in particular are not achievable with a simple blend mode.

The settings panel itself has three tabs. Appearance holds the theme mode, palette, custom themes, grid density, card shape, decorative media toggle and image effects. Layout holds the tile visibility toggles and the tile size slider. Content holds location as latitude and longitude, units as imperial or metric, API keys, video URLs, video zoom and offset, the headline source with a subreddit and rotation speed, Unsplash topic tags per tile, bookmarks and the timer duration.

So the Content tab is where the data source configuration lives, and it is separated from the appearance settings for a practical reason. Appearance is a preference and Content is a parameter, and mixing them in one panel would make both harder to scan.

All settings are managed through that panel and persisted in IndexedDB with a localStorage mirror, and they can be exported or imported as a JSON backup file. The IndexedDB choice is the right one for a page storing this much, and the localStorage mirror is there for the cases where IndexedDB is unavailable or where a quick synchronous read is needed. The export is what makes the whole thing portable: settings can be exported to a timestamped JSON file and re-imported at any time.

## Two deployments, and what the environment file reveals

The project ships in two forms, and the environment file explains the difference better than the documentation does.

The GitHub Pages deployment is described as a local-only demo with no accounts or sync, and the command is `pnpm run deploy`, with a note to update the `base` field in `vite.config.js` to match your repository name first.

The Vercel deployment is the full app, with optional auth, cloud sync and billing through serverless `/api` functions. Pushing a branch creates a Preview deployment and merging to `main` deploys Production. Cloud features activate only when environment variables are configured.

The environment file spells out the mechanics, and it is unusually well commented. There is a `VITE_LOCAL_ONLY` flag that forces local-only mode, no auth UI and no cloud sync, with settings staying in the browser, even if the cloud variables below are filled in. The comment notes this is what a self-hosted or open-source user sees by default, and that it is useful if you have pulled real credentials via `vercel env pull` but want to preview the local-only experience without deleting them.

That flag is the switch that decides which product you are running, and the documentation points at docs/environments.md for the complete Clerk, Stripe and Neon setup, the per-environment variables and the self-hosting options. That last one includes running your own sync backend with the paywall disabled via `REQUIRE_SUBSCRIPTION=false`.

So the full product is three hosted services: Clerk for accounts, Stripe for billing, and Neon for the database. The package manifest confirms it, with the Clerk backend and React packages, the Neon serverless driver, Drizzle ORM with its own kit for schema work, and Stripe. The database tooling has generate, migrate and studio scripts, and the drizzle directory and drizzle.config.ts are at the repository root.

The comments in the environment file also cover the frontend variables explicitly: the Clerk publishable key is baked into the JavaScript bundle at build time, so never put secrets there. And an optional `VITE_API_URL` for a remote API, left unset on Vercel where the API is same-origin.

That distinction between a publishable key and a secret is the one to hold on to, because it applies to any of these deployments. Anything prefixed `VITE_` ends up in the browser bundle in plain text. The real secrets live on the server side in the serverless functions under the `api/` directory.

## The stack and the build

The tech stack table lists React as the UI framework, Vite as the build tool and dev server, Tailwind CSS v4 for utility styling, Radix UI for accessible dialog, tabs, slider and switch primitives, shadcn-style components composed from Button, Card, Input and the rest, kbar for the command palette, Paper Design Shaders for the WebGL image effects, and axios for the Unsplash API requests.

The Radix plus shadcn pairing is the standard combination for accessible component primitives with a composable styling layer, and the reason to call it out is that it is what makes the custom theme editor work. Radix components take their styling from CSS variables and class names, so a theme is a set of variable values rather than a set of component overrides.

The build is driven by a Makefile rather than npm scripts alone, and the command list is worth reading because it separates the deployment targets. `make setup` installs dependencies and builds, and runs first. `make dev` starts the Vite development server and opens the browser. `make build` is the production build into `dist/`, `make build-local` builds with the base path set to the root for local serving, and `make build-vercel` builds for Vercel deployment. `make serve` builds and serves `dist/` locally on port 8000, `make preview` previews the last production build, `make deploy` deploys to GitHub Pages, `make clean` removes `dist/` and the Vite cache, `make install` installs the frontend dependencies only, and `make help` prints the targets.

The package scripts mirror those and add the rest. The build for Vercel runs a migration script before Vite, `node scripts/migrate.mjs && vite build --base "/"`, which is a reasonable thing to do but worth knowing about if you are debugging a production build. There is a typecheck with tsc, a test suite on Vitest with watch and coverage variants, and the database scripts for Drizzle.

The manifest requires Node 22 or newer and pins pnpm 11.2.2 as the package manager, and the repository has a pnpm workspace file, a Tailwind config alongside a PostCSS config, a components.json file for the shadcn component setup, and separate tsconfig files for the app and for node.

For a start page, that is a larger stack than strictly necessary, and the reason is visible in the feature list. Radix and shadcn are there for accessible components, kbar for the palette, the shader library for the image effects, and Clerk, Stripe and Neon for the cloud tier that the local build does not need at all.

## Conclusion

Adopt Modular Grid Page if you want a start page you can configure from the browser rather than from a config file, since the settings panel and the JSON export cover both casual and power use. Do not deploy the Vercel variant expecting it to work without credentials, since the cloud features only activate when the environment variables are set, and check first whether the local-only mode is enough, because it is the version a self-hosted user gets by default.

## FAQ

### What is Modular Grid Page?

It is a browser start page built as a React and Vite app, laid out as a responsive auto-sizing tile grid where every tile, theme and data source is configured through an in-app settings panel rather than by editing config files. The tiles cover photos, bookmarks, weather, a solar arc, a clock, a feature panel, decorative video and a multi-engine search box.

### How do I install and run Modular Grid Page?

Clone the repository, change into the directory and run `make setup`, which installs dependencies and produces a production build, then `make dev` to start the Vite development server. The other targets cover production builds, local serving on port 8000, previewing, deploying to GitHub Pages, cleaning and printing the target list.

### How are the settings stored in Modular Grid Page?

In IndexedDB with a localStorage mirror, and they can be exported or imported as a JSON backup file at any time. Settings are managed entirely through the in-app panel, which has three tabs: Appearance for theme and effects, Layout for tile visibility and size, and Content for location, units, API keys, media sources and bookmarks.

### Which API keys does Modular Grid Page need?

Both are optional, because the page falls back to other data sources without them. The OpenWeather key from openweathermap.org is used for current weather conditions, and the Unsplash access key from unsplash.com/developers is used for high-quality photo tiles, falling back to Wikimedia without it. Keys are entered in Settings, Content, Location and API Settings.

### What is the difference between the GitHub Pages and Vercel deployments?

The GitHub Pages build is a local-only demo with no accounts or sync, deployed with `pnpm run deploy` after setting the base field in vite.config.js. The Vercel build is the full app with optional auth, cloud sync and billing through serverless /api functions, and its cloud features activate only when the environment variables are configured.

### Is Modular Grid Page still being developed?

The repository is not archived and the last push was on 2026-09-11. There is a single GitHub release, 1.0, tagged as the Start-page MVP from January 2023, while the package manifest is at 0.1.0, and the project requires Node 22 or newer with pnpm 11.2.2 as its package manager.

## Sources

- [License: MIT](https://github.com/timh-dev/startup-page/blob/main/LICENSE)
- [Project website](https://box-start-page.vercel.app)
- [README](https://github.com/timh-dev/startup-page/blob/main/README.md)
- [Releases](https://github.com/timh-dev/startup-page/releases)
- [timh-dev/startup-page on GitHub](https://github.com/timh-dev/startup-page)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/timh-dev-startup-page
