Component Guide
This document describes the UI components in src/components/, what they do, and how they fit together.
For conventions on writing new components, see the Contributing Guide.
Component map
src/components/
├── ExternalLink.tsx # Shared: external anchor with icon
│
├── layout/ # Structural shell (always visible)
│ ├── Header.tsx # Top bar with search and logo
│ ├── Footer.tsx # Bottom bar with links
│ ├── Announcements.tsx # Dismissible banner(s)
│ ├── Error.tsx # Full-page error state
│ └── Spinner.tsx # Full-page loading state
│
└── app/ # Feature components
├── HeroSection.tsx # Landing page hero
├── SearchSection.tsx # Search bar + category selector
├── InfoCards/ # Landing page info cards
│ ├── index.tsx
│ ├── InfoCard.tsx
│ └── LinkCard.tsx
│
├── overview/
│ └── CategoryCard.tsx # Per-category summary card (overview page)
│
└── results/
├── CategoryBar.tsx # Entity type tab bar
├── CategoryBarComponent.tsx # Presentational version of CategoryBar
├── BackBar.tsx # "Back to results" navigation bar
├── ListResults.tsx # Main results list (fetches + renders)
│
├── ListResults/
│ ├── ResultItem.tsx # Single result card
│ ├── Filters.tsx # Filter panel (desktop sidebar + mobile dialog)
│ ├── Pagination.tsx # Page navigation
│ ├── NoResults.tsx # Empty state message
│ ├── SelectedFiltersBar.tsx # Active filters display (currently unused)
│ │
│ ├── ResultItem/ # ResultItem sub-components
│ │ ├── ResultItemHeader.tsx # Title, favicon, share button
│ │ ├── ResultItemInfo.tsx # All data fields (type-aware)
│ │ ├── ResultItemFooter.tsx # Sources / references section
│ │ ├── CommonDetailsFields.tsx # Fields shared across all entity types
│ │ ├── ShowMoreContainer.tsx # Collapsible field wrapper
│ │ ├── Relationships.tsx # Linked entity relationships
│ │ ├── fields/
│ │ │ ├── ResultItemField.tsx # Labelled field wrapper
│ │ │ ├── DateField.tsx # Formatted date / date range field
│ │ │ ├── FieldLabel.tsx # Field label element
│ │ │ └── RenderLinkItem.tsx # Linkified field value
│ │ └── type-specific/
│ │ └── OrganizationFields.tsx # Institution-specific fields
│ │
│ ├── Filters/
│ │ ├── FilterRenderer.tsx # Renders all filter groups
│ │ ├── MultiCheckFilter.tsx # Checkbox filter group
│ │ └── DateRangeSlider.tsx # Date range slider filter
│ │
│ └── SelectedFiltersBar/
│ └── FilterTag.tsx # Individual active filter chip
│
└── Details/
├── RelatedResults.tsx # Related items section (fetches + renders)
├── RelatedCategoryBar.tsx # Tab bar for related item categories
├── PaginatedRelatedResults.tsx # Paginated related items list
└── RelatedResultsList.tsx # Related items for a single category
Layout components
Header
Path: src/components/layout/Header.tsx
The persistent top bar shown on all pages except the landing page. Contains:
- An embedded
SearchSection(minimal variant — no category selector) - The Helmholtz KG logo, linking back to
/
No props. Always server-rendered as a client component due to SearchSection.
Footer
Path: src/components/layout/Footer.tsx
The persistent bottom bar. Contains social media links, legal links, and a copyright notice. No props.
Announcements
Path: src/components/layout/Announcements.tsx
Renders up to two dismissible banner messages: a maintenance notice and a disclaimer. Shown only when NEXT_PUBLIC_SHOW_BANNER=true.
- Dismissed state is persisted to
localStorageviauseLocalStoragewith a"daily"reset — banners reappear once per day. - Banner content is managed via the
Bannernamespace in the translation files (en.json/de.json).
No props.
Error and Spinner
Path: src/components/layout/Error.tsx, src/components/layout/Spinner.tsx
Full-page fallback states. Used by ListResults and RelatedResults when a fetch fails or is in progress. No props.
Landing page components
HeroSection
Path: src/components/app/HeroSection.tsx
The large introductory block on the landing page. Contains the tagline, intro text, and the main SearchSection. Reads content from the Intro translation namespace.
SearchSection
Path: src/components/app/SearchSection.tsx
The search bar used across the app. Supports two modes:
| Prop | Type | Default | Description |
|---|---|---|---|
theme | "dark" | "light" | "dark" | Colour scheme of border and text |
variant | "default" | "embedded" | "default" | "embedded" removes outer padding, used in Header |
isMinimal | boolean | false | Hides the category selector, used in Header |
className | string | "" | Additional CSS classes |
Key behaviours:
- On submit, navigates to
/results?category=...&searchText=... - If the category is
"all"or adetailsTextparam is present, routes to the overview page instead - The dice button picks a random entity from a hardcoded list of curated IDs and navigates directly to its detail view
- A
keyprop derived from the current URL params forces a re-mount when the search changes, resetting local state
InfoCards
Path: src/components/app/InfoCards/
Three informational cards on the landing page ("About", "Organizing Principles", "Access the Full Dataset"). Content comes from the InfoCards translation namespace. LinkCard renders an external link with an arrow icon.
Results page components
CategoryBar
Path: src/components/app/results/CategoryBar.tsx
The row of entity type tabs at the top of the results page. Reads category counts and the active category from CategoryContext. Clicking a tab navigates to /results?category=..., preserving the current searchText.
Tabs with a count of 0 are rendered but not clickable.
The rendering logic is split into two files:
CategoryBar.tsx— wires up router and context (stateful)CategoryBarComponent.tsx— pure presentational component (accepts props)
ListResults
Path: src/components/app/results/ListResults.tsx
The main orchestrator for the results page. This is where all the data-fetching and state for results, filters, and pagination lives.
What it does:
- Reads
searchText,category, and pagination state from URL params - Calls
POST /api/searchviagetSearchResults()whenever params change - Renders
Filters,Pagination(top and bottom), and a list ofResultItemcards - Shows
Spinnerwhile loading,Erroron failure,NoResultswhen the result set is empty - Caps display at 10,000 results and shows a warning banner when the total exceeds this
Filter state is stored in component state.
ResultItem
Path: src/components/app/results/ListResults/ResultItem.tsx
A single result card. Used both in the results list and on the details page.
| Prop | Type | Description |
|---|---|---|
item | SchemaOrgResultItem | The entity to display |
idx | string | Used as a React key |
category | Category (optional) | Active category — used to build the details link |
isDetailsCard | boolean (optional) | When true, renders in expanded details mode |
The card is composed of three sub-components:
| Sub-component | Responsibility |
|---|---|
ResultItemHeader | Title (links to detail view), institution favicon, bad-data warning icon, share button |
ResultItemInfo | All data fields — type-aware, shows the right fields for each entity type |
ResultItemFooter | Source URLs / references section |
ResultItemInfo field rendering:
Fields are rendered conditionally based on the entity type. Type guards from src/utils/typeGuards.ts (isCreativeWork, isPerson, isOrganization, isEvent, etc.) gate each block. Field labels come from the ResultItemIds translation namespace.
The ResultItemField wrapper component handles the label + value layout, optional ellipsis truncation, and vertical/inline display variants.
Filters
Path: src/components/app/results/ListResults/Filters.tsx
Renders the filter panel. On desktop (≥ lg) it appears as a sidebar. On mobile it renders inside a Headless UI Dialog, controlled by FilterDialogContext.
| Prop | Type | Description |
|---|---|---|
availableFilters | FilterEntity[] | Filter options returned by the API |
selectedFilters | SelectedFiltersType | Currently active filters |
setSelectedFilters | (filters: FilterEntity[]) => void | Callback to update active filters |
dateHistograms | DateHistogramData[] (optional) | Date histogram data for slider filters |
The actual rendering of each filter group is delegated to FilterRenderer, which in turn renders either MultiCheckFilter (checkbox list) or DateRangeSlider depending on the field type.
Pagination
Path: src/components/app/results/ListResults/Pagination.tsx
A thin wrapper around react-paginate.
| Prop | Type | Description |
|---|---|---|
pageCount | number | Total number of pages |
currentPage | number | Zero-based current page index |
onPageChange | (page: number) => void | Called with the new page index on navigation |
Details page components
RelatedResults
Path: src/components/app/results/Details/RelatedResults.tsx
The "Related Entries" section on the details page. Fetches related items for all 8 entity type categories in parallel using the useRelatedResults hook, then renders PaginatedRelatedResults.
| Prop | Type | Description |
|---|---|---|
id | string | The ID of the record whose related items to fetch |
Results are cached in a module-level LRU cache (max 50 entries) to avoid re-fetching when navigating back to a previously visited detail page.
PaginatedRelatedResults and RelatedResultsList
Path: src/components/app/results/Details/
PaginatedRelatedResults renders a RelatedCategoryBar (tabs for each related category with counts) and the RelatedResultsList for the active tab. RelatedResultsList renders a list of ResultItem cards for a single category, with its own pagination.
Shared components
ExternalLink
Path: src/components/ExternalLink.tsx
A standard <a> tag that opens in a new tab with rel="noopener noreferrer". Optionally renders an external link icon when pointExtern is set. Used throughout ResultItemInfo for linkified field values.
