The user-facing web application for PIC-SURE — lets researchers search, filter, and export patient cohorts from clinical and genomic datasets. Built with SvelteKit, Skeleton v3, and Tailwind CSS.
To deploy the full PIC-SURE platform, see pic-sure-all-in-one.
git clone https://github.com/hms-dbmi/PIC-SURE-Frontend
cd PIC-SURE-Frontend
pnpm install
cp .env.example .envEdit .env before starting the dev server. At minimum you need:
- Auth provider settings (
VITE_AUTH_PROVIDER_MODULE_*) — configures how users log in (e.g. Auth0 + Google OAuth2) - Auth0 tenant (
VITE_AUTH0_TENANT) - HPDS resource UUID (
VITE_RESOURCE_HPDS) — points the UI at the query engine
pnpm run devThe dev server starts at http://localhost:5173. You'll need a running PIC-SURE backend to log in and query data.
Configuration is done through environment variables in .env. See .env.example for all available options with inline comments.
Settings are grouped into a few categories:
| Category | Variables | What it controls |
|---|---|---|
| Auth providers | VITE_AUTH_PROVIDER_MODULE_* |
Login methods shown on the login page |
| Resources | VITE_RESOURCE_HPDS, VITE_RESOURCE_OPEN_HPDS, etc. |
Which backend resources the UI queries |
| Pages & features | VITE_OPEN, VITE_DASHBOARD, VITE_VARIANT_EXPLORER, etc. |
Which pages and features are enabled |
| Theming | VITE_LOGO, VITE_DOTS_COLORS_CLASS |
Logo and login page appearance |
| Export | VITE_ALLOW_EXPORT, VITE_ALLOW_DOWNLOAD, etc. |
Data export behavior |
| Analytics | VITE_GOOGLE_ANALYTICS_ID, VITE_GOOGLE_TAG_MANAGER_ID |
Tracking (leave blank to disable) |
For full configuration documentation, see the PIC-SURE Frontend Configuration Guide.
Some PIC-SURE text is configurable. We provide a configuration file that is used for custom text elements here.
Feature flags, settings, and branding are also fetched from the PICSURE API (GET:/picsure/configuration) and cached server-side on startup (src/hooks.server.ts's init, src/lib/server/configCache.ts). Each request's SSR render and the client's hydration both resolve config from that same server-side cache: src/routes/+layout.server.ts reads it and passes it to src/routes/+layout.ts, which applies it to configuration.svelte.ts's reactive config object via applyConfig() before anything renders. This mirrors the "bootstrap" pattern feature-flag services like LaunchDarkly and GrowthBook use to avoid a flash of default values on first load: the server evaluates once, and the client hydrates from that exact same payload instead of re-fetching. The cache is also available directly at GET:/api/config, and can be forced to refresh via GET:/api/config/refresh.
Features and settings are each resolved from up to three layers: hardcoded defaults, individual VITE_<KEY> env vars (see .env.example), and rows fetched from the API.
VITE_API_CONFIG_FEATURES/VITE_API_CONFIG_SETTINGS/VITE_API_CONFIG_BRANDINGset thekindused to fetch each category fromGET:/picsure/configuration(defaults:ui:featureFlag,ui:setting,ui:branding). Leave one blank to skip fetching that category from the API entirely, relying on env vars and defaults only.VITE_CONFIG_MODEdecides who wins when both an env var and an API row are set for the same field:seed(default): defaults -> env (if set) -> API (if set) winsoverride: defaults -> API (if set) -> env (if set) wins
- "Set" means a non-blank value for boolean/number/JSON-typed fields (a blank value falls back to the default, since blank isn't a valid number or JSON and isn't distinguishable from "off") - see
.env.example's Config precedence section. String-typed fields are the exception: an explicit blank is honored as a deliberate override, not "unset".
Branding is the exception to this layering. It defaults to whatever is in configuration.json, and only a narrow set of fields — currently just the logo (VITE_LOGO/LOGO and VITE_LOGO_ALT/LOGO_ALT) — can be seeded/overridden via env var or the API. Everything else under branding (sitemap, footer, landing page copy, etc.) is sourced from configuration.json and isn't wired up to env/API resolution at this time.
This system is under active development and subject to change.
- e2e (Playwright): use
mockApiConfig(page, { features, settings, branding })fromtests/end-to-end/custom-context.tsto set per-test feature/setting/branding rows. It works by seeding a cookie thatsrc/routes/+layout.server.tsreads and applies to thatBrowserContext's requests only — real config, real backend calls, and other concurrent tests are unaffected. The override is only honored when the app is built/served with--mode test(how Playwright'swebServeralways runs it); it's inert in dev and production builds. - Component tests (Vitest): call
applyConfig(rows)(from$lib/configuration.svelte) with rawConfigObject[]rows to exercise the real API-row mapping/precedence pipeline, or mutateconfig.features/config.settings/config.brandingfields directly for values not resolvable from API rows (e.g.config.branding.explorePage.resultInfo, sourced fromconfiguration.json).tests/component/setup.tscallsresetConfig()after every test automatically, so tests don't need to clean up after themselves.
- SvelteKit — framework (routing, SSR, layouts)
- Skeleton v3 — UI component library
- Tailwind CSS — utility-first styling
- Playwright — integration testing
- Vitest — unit and component testing
Routes use SvelteKit's layout groups to enforce access control at each level:
src/routes/
├── (authentication)/ # Login pages
└── (picsure)/ # Main app shell
├── (public)/ # Open-access routes (explorer, discover, help, dashboard)
└── (authorized)/ # Requires a valid auth token
└── (admin)/ # Admin-only (user management, roles, connections)
- (public) routes are what users see in
openandexploreauth modes — accessible without logging in. - (authorized) routes require a valid token. The layout file at this level checks token presence and expiration, redirecting to login if needed.
- (admin) routes are restricted to users with admin privileges.
Auth is enforced by layout files (+layout.ts) at each group level, using SvelteKit's cascading layout pattern.
src/lib/
├── components/ # Feature-scoped UI (explorer, admin, query, plots, etc.)
├── services/ # API communication
├── stores/ # Svelte stores (user state, query state, etc.)
├── models/ # TypeScript types
├── auth/ # Authentication utilities
└── utilities/ # Helper functions
| Command | What it does |
|---|---|
pnpm run dev |
Start dev server with hot reload |
pnpm run build |
Production build |
pnpm run check |
Svelte and TypeScript checks |
pnpm run format |
Format code with Prettier |
pnpm run lint |
Lint with Prettier + ESLint |
pnpm run test |
Run all tests (unit + component + integration) |
Run all checks before submitting a PR:
pnpm run format && pnpm run lint && pnpm run check && pnpm run testIf this is your first time running integration tests, install Playwright browsers:
pnpx playwright installPlease refer to CONTRIBUTING for guidelines on how to contribute, submit issues, and propose improvements.
Also refer to our Code of Conduct.