Terrae new
End-to-end workflow for building a new Terrae component from scratch
git clone https://github.com/alamenai/terrae
T=$(mktemp -d) && git clone --depth=1 https://github.com/alamenai/terrae "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/new" ~/.claude/skills/alamenai-terrae-new && rm -rf "$T"
.claude/skills/new/SKILL.mdCreate New Component Skill
End-to-end workflow for building a new Terrae component from scratch.
Covers all 8 outputs:
- Component source
- Registry
- Exports
- Documentation
- Examples
- Sidebar
- Components page
- Changelog
Component structure, patterns, responsiveness, and performance rules are in
.claude/rules/react/component.md.
All Outputs
| # | Output | File(s) |
|---|---|---|
| 1 | Component source file | |
| 2 | Barrel export | (update) |
| 3 | Registry entry | (update) |
| 4 | Example file(s) | |
| 5 | Documentation page | |
| 6 | Sidebar navigation | (update) |
| 7 | Components listing | (update) |
| 8 | Changelog entry | (update) |
Instructions
When the developer requests a new component:
Step 1: Gather Requirements
Ask for:
- Component name (e.g.,
,MapHeatmap
)MapPolygon - Core functionality
- Whether it needs compound components (like
,MarkerContent
)MarkerPopup - Category:
or"core"
(most components are features)"features" - Lucide icon for the sidebar and components page
- Whether it exposes a control hook (e.g.,
)useHeatmapControl
If there are multiple valid implementation approaches (e.g., Mapbox layers vs DOM overlay, canvas vs CSS animations, GeoJSON source vs custom rendering), present the options with trade-offs and let the developer choose before writing code.
Step 2: Create the Component File and Export
Follow the map component rules in
.claude/rules/react/component.md for the component structure, template, patterns, and barrel export.
- Location:
src/registry/map/{component-name}.tsx - Use kebab-case for file names (e.g.,
)heat-map.tsx - Export from
src/registry/map/index.tsx
Step 3: Add Registry Entry
Update
registry.json by adding an entry to the items array.
Follow this structure:
{ "name": "heat-map", "type": "registry:ui", "title": "Map Heatmap", "description": "Short description of the component.", "dependencies": ["mapbox-gl"], "devDependencies": ["@types/mapbox-gl"], "registryDependencies": ["https://www.terrae.dev/map.json"], "files": [ { "path": "src/registry/map/heat-map.tsx", "type": "registry:ui", "target": "components/ui/map/heat-map.tsx" } ] }
Key rules:
uses kebab-case with ``prefix (e.g.,name
)heat-map
always includesregistryDependencies
(the core["https://www.terrae.dev/map.json"]
component that all other components depend on)Map- Add extra
only if the component needs packages beyonddependenciesmapbox-gl - Components that don't need
directly can have emptymapbox-gl
(e.g., watermark)dependencies
Step 4: Create Example File(s)
Location:
src/app/docs/_components/examples/{name}-example.tsx
Use kebab-case for the file name.
The basic example should demonstrate the simplest usage of the component.
import { Map, MapHeatmap } from "@/registry/map" export const HeatmapExample = () => { const accessToken = process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN || "" return ( <div className="h-full w-full"> <Map accessToken={accessToken} center={[-74.006, 40.7128]} zoom={10}> <MapHeatmap id="heatmap-basic" {/* ...minimal props */} /> </Map> </div> ) }
Key rules:
- Only add
if the example uses hooks, event handlers, or browser APIs — purely compositional examples that just render map components don't need it"use client" - Import from
@/registry/map - Wrap the map in a
<div className="h-full w-full"> - Pass
as the access token — theprocess.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN || ""
component validates it and shows an error if missing, so examples don't need their own checkMap - Export the component with a descriptive name (PascalCase)
- Create additional example files for each variation (e.g.,
,heatmap-color-example.tsx
)heatmap-custom-example.tsx
Step 5: Create Documentation Page
Location:
src/app/docs/{slug}/page.tsx
Use the
lines-animated/page.tsx as the gold standard reference.
The slug should match the sidebar href (e.g.,
/docs/heatmap → src/app/docs/heatmap/page.tsx).
import { DocsLayout, DocsSection, DocsCode, DocsLink } from "../_components/docs" import { ComponentPreview } from "../_components/component-preview" import { CodeBlock } from "../_components/code-block" import { HeatmapExample } from "../_components/examples/heatmap-example" import { getExampleSource } from "@/lib/get-example-source" import { Metadata } from "next" import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" export const metadata: Metadata = { title: "Heatmap", } const HeatmapPage = () => { const basicSource = getExampleSource("heatmap-example.tsx") return ( <DocsLayout title="Heatmap" description="Short description of what the component does." prev={{ title: "Previous Component", href: "/docs/previous" }} next={{ title: "Next Component", href: "/docs/next" }} > <DocsSection title="Installation"> <p>First, make sure you have the base map component installed:</p> <CodeBlock code={`npx shadcn@latest add https://www.terrae.dev/map.json`} language="bash" /> <p className="mt-4">Then install the heatmap component:</p> <CodeBlock code={`npx shadcn@latest add https://www.terrae.dev/heat-map.json`} language="bash" /> </DocsSection> <ComponentPreview code={basicSource}> <HeatmapExample /> </ComponentPreview> {/* Additional sections with examples, props tables, etc. */} </DocsLayout> ) } export default HeatmapPage
Key rules:
- Always export
with ametadatatitle - Use
withDocsLayout
,title
,description
, andprev
navigation linksnext - First section is always "Installation" with two
s (base map + component)CodeBlock - The basic
goes directly after Installation (no section title or description, just the demo)ComponentPreview - Use
to wrap each example with its source codeComponentPreview - Use
to load example source codegetExampleSource("filename.tsx") - Use
for inline code references in descriptionsDocsCode - Use
with aDocsSection
for each sectiontitle - Add a props
when the component has many configurable propsTable - Set
/prev
to match adjacent items in the sidebar navigationnext
Step 6: Add to Sidebar
Update
src/app/docs/_components/docs-sidebar.tsx:
- Import the Lucide icon at the top (if not already imported)
- Add a
entry in the correct section of theNavItem
arraynavigation - Add
badge: "new"
{ title: "Heatmap", href: "/docs/heatmap", icon: Flame, badge: "new" },
Sections:
— Story, Changelog"Explore"
— Introduction, Installation, Comparison, Components, Hooks, Reference"Get Started"
— Map, Controls, Compass, Marker, Popup"Core"
— Everything else"Features"
Step 7: Add to Components Page
Update
src/app/docs/components/page.tsx:
- Import the Lucide icon at the top (if not already imported)
- Add a
entry to theComponentItem
arraycomponents - Add
isNew: true
{ title: "Heatmap", href: "/docs/heatmap", description: "Short description matching the registry description", icon: Flame, category: "features", installCommand: "npx shadcn@latest add https://www.terrae.dev/heat-map.json", isNew: true, },
Key rules:
iscategory
or"core"
(must match the sidebar section)"features"
URL follows the patterninstallCommandhttps://www.terrae.dev/{registry-name}.json- Add
if the component only works with Mapbox GL (not MapLibre)mapboxOnly: true - Place the entry near similar components in the array
Step 8: Update Changelog
Update
src/app/docs/changelog/page.tsx:
Add a new entry to the
components array of the most recent (topmost) ChangelogEntry in the changelogs array:
{ title: "Heatmap", description: ( <> New <code className="rounded bg-muted px-1 py-0.5 text-xs">MapHeatmap</code> component for visualizing data density on the map. Supports customizable color ramps, radius control, and intensity adjustment. </> ), href: "/docs/heatmap", },
Key rules:
- Add under
for new components,components
for new features,features
for bug fixes,fixes
for new propsproperties - The
uses JSX with inlinedescription
tags for component names<code> - Always include
linking to the docs pagehref
Step 9: Review with User
Before finalizing, show all changes:
- The component source code
- The barrel export addition
- The registry entry
- The example file(s)
- The documentation page
- The sidebar entry
- The components page entry
- The changelog entry