A JSON templating system for geospatial visualization using the @@ prefix convention. Define parameterized MapLibre/deck.gl map configurations in plain JSON and resolve them at runtime with live controls.
Built as an internal Vizzuality showcase.
VizzJson introduces a set of prefixes that turn static JSON into dynamic, parameterized configurations:
| Prefix | Purpose | Example |
|---|---|---|
@@#params.X |
Runtime parameter (dot notation) | "opacity": "@@#params.opacity" |
@@function: |
Call a registered function | "@@function:setQueryParams" |
@@type: |
Instantiate a deck.gl layer or component | "@@type:ScatterplotLayer" |
@@=[expr] |
Evaluate a JS expression | "@@=[props.value * 2]" |
@@#GL. |
OpenGL constant | "@@#GL.POINTS" |
{
"config": {
"source": {
"type": "raster",
"tiles": ["https://tiles.example.com/{z}/{y}/{x}.jpg"]
},
"styles": [
{
"type": "raster",
"paint": { "raster-opacity": "@@#params.opacity" },
"layout": { "visibility": "@@#params.visibility" }
}
]
},
"params_config": [
{ "key": "opacity", "default": 0.8, "min": 0, "max": 1, "step": 0.05 },
{
"key": "visibility",
"default": "visible",
"options": ["visible", "none"]
}
]
}The playground auto-infers UI controls from params_config — sliders for numeric ranges, dropdowns for options, color pickers for hex values, switches for booleans.
JSON config
→ parse & extract params_config
→ render parameter controls
→ user adjusts params
→ Stage 1: resolveParams() — replaces @@#params.* with values
→ Stage 2: JSONConverter — resolves @@function:, @@type:, @@=[], @@#GL.
→ map renders + legend resolves
All resolution is immutable — original configs are never mutated.
pnpm install
pnpm dev # Dev server on http://localhost:3000| Command | Description |
|---|---|
pnpm dev |
Dev server on :3000 |
pnpm build |
Production build |
pnpm test |
Run all tests (vitest) |
pnpm test:watch |
Tests in watch mode |
pnpm lint |
ESLint check |
pnpm format |
Prettier check |
pnpm check |
Auto-fix lint + formatting |
pnpm typecheck |
TypeScript type checking |
| Route | Description |
|---|---|
/ |
Landing page |
/playground |
Interactive editor with Monaco, parameter controls, map renderer, and legend panel |
/presentation |
Full-screen slide deck walking through the system |
/guidelines |
Documentation for params, functions, types, expressions, and legends |
src/
├── lib/converter/ # Core resolution engine
│ ├── index.ts # resolveConfig() entry point
│ ├── params-resolver.ts # Stage 1: @@#params.* resolution
│ └── converter-config.ts # Stage 2: deck.gl JSONConverter
├── lib/param-inference.ts # Auto-detect UI control type from param config
├── lib/types.ts # Core types (LayerSchema, ParamConfig, etc.)
├── examples/ # 9 progressive JSON configs (basic → advanced)
├── hooks/use-converter.ts # React hook wrapping resolveConfig()
├── components/
│ ├── playground/ # Monaco editor, map, param controls, legend
│ ├── presentation/ # Slide deck components
│ ├── legends/ # Basic, choropleth, gradient legend components
│ ├── landing/ # Landing page sections
│ └── resolver-components/ # React components registered as @@type targets
└── routes/ # File-based routing (TanStack Router)
- Framework: TanStack Start (full-stack React 19) with file-based routing
- Mapping: MapLibre GL + deck.gl via react-map-gl
- Editor: Monaco Editor
- Styling: Tailwind CSS v4 + shadcn/ui (base-nova)
- Testing: Vitest + Playwright
- i18n: Paraglide.js
The playground includes 9 progressive examples in src/examples/:
| # | Example | Tier |
|---|---|---|
| 01 | Raster Opacity & Visibility | Basic |
| 02 | Vector Fill Color | Intermediate |
| 03 | Choropleth Match Expression | Intermediate |
| 04 | Graduated Interpolate | Intermediate |
| 05 | Classified Step Expression | Intermediate |
| 06 | Data-Driven Circles | Intermediate |
| 07 | Raster with Registered Functions | Advanced |
| 09 | Conditional Case Expression | Advanced |
| 10 | React Component Composition | Advanced |
Tests live in tests/ mirroring the src/ structure. Core converter logic has full coverage — params resolution, function calls, immutability verification, nested object/array handling.
pnpm test # All tests
pnpm test -- tests/lib/converter/functions.test.ts # Single fileThe core resolution engine is published to npm as @vizzuality/vizz-json.
npm install @vizzuality/vizz-jsonIt exposes two entry points:
| Entry | Import | Description |
|---|---|---|
| Main | @vizzuality/vizz-json |
resolveConfig(), resolveParams(), types |
| React | @vizzuality/vizz-json/react |
React hook and components |
Releases are automated via Release Please. Merging to main triggers a release PR; merging the release PR publishes to npm and creates a GitHub release.
The package changelog lives at packages/vizz-json/CHANGELOG.md.
MIT