React apps Integration
This page covers how to integrate React components into your repository, including configuring entry points, building bundles, and mounting components in Jinja/JinjaX templates.
For an overview of the React architecture in NRP repositories, see the Overview page.
Repository-Level Component Structure
Repository-wide React components are typically placed in ui/components/:
- index.js
- Component.jsx
- component-util.js
- index.js
- home-page-search.js
- custom-components.js
- webpack.py
Creating a New Component
Add Mount Point to Template
First, declare a DOM mount point in your Jinja template where the React component will be rendered:
<div id="welcome-banner" data-user='{{ user | tojson }}'></div>The id attribute is used by the JavaScript entry point to locate the element, and data-* attributes pass server-side data to the React component.
Create the Component
Create a simple React component that displays a welcome message:
import React from "react";
export function WelcomeBanner({ user }) {
return (
<div className="ui message info">
<i className="info circle icon"></i>
<div className="content">
<div className="header">
Welcome, {user.name || "Guest"}!
</div>
<p>This is your custom React component.</p>
</div>
</div>
);
}Create the Entry Point
Create an entry point that mounts your component to the DOM:
import ReactDOM from "react-dom";
import React from "react";
import { WelcomeBanner } from "./WelcomeBanner";
function renderWelcomeBanner() {
const element = document.getElementById("welcome-banner");
if (element) {
const user = JSON.parse(element.dataset.user || "{}");
ReactDOM.render(
<WelcomeBanner user={user} />,
element
);
}
}
// Initialize when DOM is ready
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", renderWelcomeBanner);
} else {
renderWelcomeBanner();
}Configure Entry Points in webpack.py
Add the entry point to your webpack configuration:
from invenio_assets.webpack import WebpackThemeBundle
theme = WebpackThemeBundle(
__name__,
".",
default="semantic-ui",
themes={
"semantic-ui": dict(
entry={
"welcome_banner": "./js/WelcomeBanner/index.js",
},
aliases={},
dependencies={},
devDependencies={},
)
},
)Include Bundle in Template
Include the bundled JavaScript in your Jinja template:
<div id="welcome-banner" data-user='{{ user | tojson }}'></div>
{%- block javascript %}
{{ webpack['welcome_banner.js'] }}
{%- endblock %}Extending templates with inheritance: When your template extends another page that also defines a javascript block, use {{ super() }} to preserve the parent’s JavaScript bundles:
{%- block javascript %}
{{ super() }}
{{ webpack['welcome_banner.js'] }}
{%- endblock %}This ensures that any JavaScript bundles defined in the parent template (such as search UI, theme scripts, etc.) are still loaded in addition to your custom bundle.
Rebuild JavaScript Assets
After adding or modifying entry points, rebuild the JavaScript bundles:
./run.sh invenio webpack clean create
./run.sh invenio webpack installData Passing with data-* Attributes
Pass server-side data to React components using HTML data attributes. The server-side data comes from the template context prepared by UI resource view methods:
UI resource views (see UI Resource Views) prepare context data that gets passed to templates. You can pass any available context variable to your React component using data attributes.
The tojson filter serializes Python objects to JSON:
<div id="welcome-banner"
data-user-name="{{ user.name }}"
data-user-email="{{ user.email }}"
data-settings='{{ settings | tojson }}'>
</div>
{%- block javascript %}
{{ webpack['welcome_banner.js'] }}
{%- endblock %}Use data-* attributes for simple values and | tojson for complex objects like nested data structures or arrays.
Multiple Apps on One Page
You can embed multiple React apps in a single page. Each component has its own DOM mount point and entry point:
<div id="welcome-banner" data-user='{{ user | tojson }}'></div>
<div id="recent-items" data-items='{{ recent_items | tojson }}'></div>
{%- block javascript %}
{{ webpack['welcome_banner.js'] }}
{{ webpack['recent_items.js'] }}
{%- endblock %}Each entry point independently renders to its designated DOM element.
Avoid mounting multiple apps to the same DOM element, as this causes state conflicts.