Skip to Content

Search page

The search page displays a list of records matching user queries. It uses react-searchkit to provide faceted search, sorting, and pagination with customizable result item components.

Request and Application Flow

Request Flow

Application Flow

The search application is organized into server-side and client-side layers:

Server-side, the UI Resource View handles the search page request and provides search_app_config containing the search endpoint, facets, sort options, and other configuration. The record_search.html template renders a <div> with data-invenio-search-config attribute containing this JSON configuration and includes the webpack bundle.

Client-side, the Webpack Entry Point reads the config, uses parseSearchAppConfigs() and createSearchAppsInit() to mount the react-searchkit app, and registers your component overrides (like ResultsListItem). The react-searchkit library renders SearchAppLayout which contains facets (SearchAppFacets) and results area (SearchAppResults). The results area renders ResultsList which loops through search results and renders each with your customized ResultsListItem component.

Key Files

FileLocationPurpose
Search templateoarepo_ui.record_searchoarepo-ui 
Results list item{{model_name}}/semantic-ui/js/{{model_name}}/search/ResultsListItem.jsxModel-specific (nrp-model-copier )
Search index{{model_name}}/semantic-ui/js/{{model_name}}/search/index.jsModel-specific
Search componentsoarepo_ui/searchoarepo-ui 
    • webpack.py

Configuration

UI Resource

The search routes are defined in your model’s UI resource config:

ui/mymodel/__init__.py
from oarepo_ui.resources.records.config import RecordsUIResourceConfig from oarepo_ui.theme.config import UIComponent, UIComponentImportMode class MymodelUIResourceConfig(RecordsUIResourceConfig): blueprint_name = "mymodel" url_prefix = "/mymodel" routes = { "search": "", # ... other routes } # Critical: Required for global search UI to work search_component = UIComponent( "DatasetsResultsListItem", "@js/mymodel/search/ResultsListItem", UIComponentImportMode.DEFAULT, )

The search_component declares the React component used to render search result items for your record model. This is critical for the global search UI to work - the global search displays results from multiple record models and needs to know which component to use for each.

The search_component automatically registers your result item component with the override system using the overridable component key. For more on server-side component overrides, see Component Overrides.

Search App Configuration

The search app configuration is dynamically generated by RecordsUIResourceConfig.search_app_config() method using oarepo-model’s presets.

For detailed information on how to customize search behavior through oarepo-model presets and customizations (facets, mappings, analyzers), see Search configuration.

Template Context

The search page receives context variables from the UI resource view. If no custom template is configured, the system uses the default page component (oarepo_ui.pages.RecordSearch), which extends your model’s base record_search.html template.

The following context variables are available:

VariableDescription
themeTheme configuration
search_app_configSearch app configuration (endpoint, facets, sort options)
ui_configUI model configuration
ui_resourceReference to the UI resource instance
ui_linksUI links
extra_contextAdditional context from resource components
webpack_entryWebpack entry point for the search app JavaScript

The default component (oarepo_ui/pages/RecordSearch.jinja) declares these variables in a {# def #} block and extends your model’s base template ({model_name}/record_search.html), which is provided by nrp-model-copier and extends oarepo_ui/record_search.html.

Building Your Search Result Item

The nrp-model-copier template provides a ResultsListItem component that displays each search result. You customize this component to match your record’s metadata. For available search components, see Search UI Components.

ui/{{model_name}}/semantic-ui/js/{{model_name}}/search/ResultsListItem.jsx
import React from "react"; import PropTypes from "prop-types"; import _get from "lodash/get"; import _join from "lodash/join"; import { Grid, Item, Label } from "semantic-ui-react"; import { i18next } from "@translations/i18next"; export const ResultsListItem = ({ result, ...rest }) => { const accessRights = _get(result, "ui.access_status", null); const createdDate = _get( result, "ui.created_date_l10n_short", "No creation date found." ); const languages = _get(result, "metadata.languages", []); const version = _get(result, "metadata.version", null); const title = _get(result, "metadata.title", i18next.t("No title")); return ( <Item key={result.id} data-testid="result-item"> <Item.Content> <Grid> <Grid.Row> <Grid.Column className="results-list item-main"> <div className="justify-space-between flex"> <Item.Header as="h2"> <a href={result.links.self_html}>{title}</a> </Item.Header> <div className="item-access-rights"> {result.state && ( <Label title={result.state_timestamp}>{result.state}</Label> )} {accessRights && accessRights.id !== "open" && ( <Label title={`${accessRights.description_l10n}`}> {accessRights.title_l10n} </Label> )} </div> </div> <Item.Meta> <Grid columns={1}> <Grid.Column> <Grid.Row className="ui separated"> <span aria-label={i18next.t("Languages")} title={i18next.t("Languages")} > {_join(languages.map((l) => l.title), ", ")} </span> </Grid.Row> </Grid.Column> </Grid> </Item.Meta> <Item.Extra> <div> <small> <p> {createdDate && ( <> {i18next.t("Uploaded on")} <span>{createdDate}</span>{" "} {version && `(${i18next.t("version")}: ${version})`} </> )} </p> </small> </div> </Item.Extra> </Grid.Column> </Grid.Row> </Grid> </Item.Content> </Item> ); }; ResultsListItem.propTypes = { result: PropTypes.object.isRequired, }; export default ResultsListItem;

Component Overrides

Components can be overridden server-side via UIComponent (recommended) or client-side in JavaScript. For more on server-side overrides, see Component Overrides.

Client-side overrides:

ui/{{model_name}}/semantic-ui/js/{{model_name}}/search/index.js
import { parseSearchAppConfigs, createSearchAppsInit, } from "@js/oarepo_ui/search"; import ResultsListItem from "./ResultsListItem"; /** NOTE: This reads configs for any search app present on a page * In HTML/Jinja, a search app instance is represented * by a configuration object in the data-invenio-search-config attribute */ const [{ overridableIdPrefix }] = parseSearchAppConfigs(); export const componentOverrides = { /** NOTE: Then you can replace any existing search ui * component with your own implementation, e.g.: */ [`${overridableIdPrefix}.ResultsList.item`]: ResultsListItem, }; createSearchAppsInit({ componentOverrides });

Common override keys:

KeyDescription
{Model}.Search.ResultsList.itemIndividual result item
{Model}.Search.SearchApp.resultsMain results container
{Model}.Search.SearchApp.facetsFacets panel
{Model}.Search.SearchApp.searchbarSearch bar component

Optional: Customize Search Page Template

By default, the search page uses the default page component (oarepo_ui.pages.RecordSearch), which extends your model’s base record_search.html template. You only need to create a custom template if you want to customize the page layout or add custom content.

When you use the templates configuration to specify a custom search template, you are replacing the default JinjaX page component with your own. Your custom template must be a valid JinjaX component with a proper {# def #} block declaring all context variables.

For the search page, the following context variables must be declared in the {# def #} block: search_app_config, ui_config, ui_resource, ui_links, webpack_entry, and extra_context. Without the {# def #} block, JinjaX will not recognize these variables and the template will fail to render.

Step 1: Create the JinjaX page component

Add a search tips banner that shows only on the first visit:

ui/mymodel/templates/semantic-ui/mymodel/RecordSearch.jinja
{# def search_app_config, extra_context, model_name, ... #} {% extends model_name ~ "/record_search.html" %}
ui/mymodel/templates/semantic-ui/mymodel/record_search.html
{% extends "oarepo_ui/record_search.html" %} {% block page_body %} {# Show tips banner only if no search query #} {% if not search_app_config.get('initialQuery') %} <div class="ui message info"> <i class="icon info circle"></i> {{ _("Tip: Use quotes for exact phrases, OR to combine terms") }} </div> {% endif %} {{ super() }} {% endblock %}

Step 2: Register the template

In your UI resource config, specify the custom template:

ui/mymodel/__init__.py
class MymodelUIResourceConfig(RecordsUIResourceConfig): # ... other config ... templates = { "search": "mymodel.RecordSearch", }

Note the dot notation: mymodel.RecordSearch refers to the JinjaX component at mymodel/RecordSearch.jinja.

JinjaX Component Required: When using templates configuration, you must create a valid JinjaX page component (e.g., mymodel/RecordSearch.jinja) with a {# def #} block declaring all context variables. The component should extend your model’s base template. Without the {# def #} block, the template will fail to render.

Custom JavaScript

Add model-specific JavaScript to the search page without overriding the entire template:

Create ui/mymodel/templates/semantic-ui/mymodel/record_search/javascript.html:

ui/mymodel/templates/semantic-ui/mymodel/record_search/javascript.html
<script> // Custom search behavior document.addEventListener('DOMContentLoaded', function() { console.log('Custom search page behavior'); }); </script>

The base template automatically includes this file if it exists at {model_name}/record_search/javascript.html.

Further Reading

Last updated on