Skip to main content

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

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.


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 localStorage via useLocalStorage with a "daily" reset — banners reappear once per day.
  • Banner content is managed via the Banner namespace 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:

PropTypeDefaultDescription
theme"dark" | "light""dark"Colour scheme of border and text
variant"default" | "embedded""default""embedded" removes outer padding, used in Header
isMinimalbooleanfalseHides the category selector, used in Header
classNamestring""Additional CSS classes

Key behaviours:

  • On submit, navigates to /results?category=...&searchText=...
  • If the category is "all" or a detailsText param 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 key prop 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/search via getSearchResults() whenever params change
  • Renders Filters, Pagination (top and bottom), and a list of ResultItem cards
  • Shows Spinner while loading, Error on failure, NoResults when 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.

PropTypeDescription
itemSchemaOrgResultItemThe entity to display
idxstringUsed as a React key
categoryCategory (optional)Active category — used to build the details link
isDetailsCardboolean (optional)When true, renders in expanded details mode

The card is composed of three sub-components:

Sub-componentResponsibility
ResultItemHeaderTitle (links to detail view), institution favicon, bad-data warning icon, share button
ResultItemInfoAll data fields — type-aware, shows the right fields for each entity type
ResultItemFooterSource 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.

PropTypeDescription
availableFiltersFilterEntity[]Filter options returned by the API
selectedFiltersSelectedFiltersTypeCurrently active filters
setSelectedFilters(filters: FilterEntity[]) => voidCallback to update active filters
dateHistogramsDateHistogramData[] (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.

PropTypeDescription
pageCountnumberTotal number of pages
currentPagenumberZero-based current page index
onPageChange(page: number) => voidCalled 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.

PropTypeDescription
idstringThe 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

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.