Merits Block
Merits is a marketing benefits section built on MUI with a centered header, a row of merit tiles, and a primary call-to-action link button.
Installation
- npm
- yarn
- pnpm
- bun
npm install @nodeblocks/frontend-merits-block
yarn add @nodeblocks/frontend-merits-block
pnpm add @nodeblocks/frontend-merits-block
bun add @nodeblocks/frontend-merits-block
What You Need
| Item | Why it matters |
|---|---|
subtitle | Supporting text line |
meritsTitle | Main heading |
items | Merit tiles content |
buttonText | Label on the CTA |
buttonHref | href for the default contained button |
Pass header copy, merit items, and CTA text from your page or CMS. Each item needs imageUrl, text, and subtext. Images use object-fit: contain inside a fixed aspect-ratio frame on small and large screens.
Code Examples
- Quick Start
- Labels and URLs
- Compound Components
- Block Override
function Example() { const items = [ { imageUrl: '/img/undraw_docusaurus_mountain.svg', text: 'Fast Delivery', subtext: 'Get your orders delivered quickly and efficiently', }, { imageUrl: '/img/undraw_docusaurus_react.svg', text: 'Quality Guarantee', subtext: 'We ensure the highest quality for all our products', }, { imageUrl: '/img/undraw_docusaurus_tree.svg', text: '24/7 Support', subtext: 'Round-the-clock customer support for all your needs', }, ]; return ( <Merits subtitle="Why Choose Us" meritsTitle="Our Key Benefits" buttonText="Learn More" buttonHref="#about" items={items} /> ); }
Customize header copy, merit tile content, and the CTA destination.
function Example() { const items = [ { imageUrl: '/img/undraw_docusaurus_mountain.svg', text: 'Ship together', subtext: 'Collaborate on a shared codebase with clear reviews and fast iteration.', }, { imageUrl: '/img/undraw_docusaurus_react.svg', text: 'Stay organized', subtext: 'Keep tasks and checklists in sync so everyone knows what happens next.', }, { imageUrl: '/img/undraw_docusaurus_tree.svg', text: 'Clear communication', subtext: 'Give and receive feedback in one place so nothing gets lost in the noise.', }, ]; return ( <Merits subtitle="Built for product teams" meritsTitle="Everything you need to move faster" buttonText="View pricing" buttonHref="#pricing" items={items} /> ); }
Set subtitle, meritsTitle, buttonText, buttonHref, and items on Merits. Sub-components read from context; override layout or styling with compound children, or replace blocks with a function child.
Compose Merits.Header, Merits.Content, and Merits.ActionBar directly and style each block with sx.
function Example() { const items = [ { imageUrl: '/img/undraw_docusaurus_mountain.svg', text: 'Fast Delivery', subtext: 'Get your orders delivered quickly and efficiently', }, { imageUrl: '/img/undraw_docusaurus_react.svg', text: 'Quality Guarantee', subtext: 'We ensure the highest quality for all our products', }, { imageUrl: '/img/undraw_docusaurus_tree.svg', text: '24/7 Support', subtext: 'Round-the-clock customer support for all your needs', }, ]; return ( <Merits subtitle="Why Choose Us" meritsTitle="Our Key Benefits" buttonText="Learn More" buttonHref="#about" items={items} sx={{ maxWidth: 960, mx: 'auto', px: 2 }} > <Merits.Header sx={{ px: 2, py: 3, borderRadius: 2, bgcolor: 'grey.50', '& .nbb-merits-merit-subtitle': { letterSpacing: 1, textTransform: 'uppercase' }, }} /> <Merits.Content sx={{ '& .nbb-merits-merit-item': { p: 2, borderRadius: 2, border: '1px solid', borderColor: 'divider', bgcolor: 'grey.50' }, '& .MuiDivider-root': { display: 'none' }, }} /> <Merits.ActionBar> <Merits.ActionBar.Button sx={{ px: 5, borderRadius: 99 }}> Get started today </Merits.ActionBar.Button> </Merits.ActionBar> </Merits> ); }
Use function children with defaultBlocks and defaultBlockOrder to inject blocks, replace defaults, and control order.
function Example() { const items = [ { imageUrl: '/img/undraw_docusaurus_mountain.svg', text: 'Fast Delivery', subtext: 'Get your orders delivered quickly and efficiently', }, { imageUrl: '/img/undraw_docusaurus_react.svg', text: 'Quality Guarantee', subtext: 'We ensure the highest quality for all our products', }, { imageUrl: '/img/undraw_docusaurus_tree.svg', text: '24/7 Support', subtext: 'Round-the-clock customer support for all your needs', }, ]; return ( <Merits subtitle="Why Choose Us" meritsTitle="Our Key Benefits" buttonText="Learn More" buttonHref="#about" items={items} sx={{ maxWidth: 960, mx: 'auto', px: 2 }} > {({ defaultBlocks, defaultBlockOrder }) => ({ blocks: { ...defaultBlocks, promo: ( <Alert severity="success" sx={{ textAlign: 'center' }}> New customers get extended onboarding at no extra cost. </Alert> ), content: ( <Merits.Content sx={{ p: 2, borderRadius: 2, bgcolor: 'background.paper', border: '1px solid', borderColor: 'divider', }} /> ), }, blockOrder: ['header', 'promo', 'content', 'actionBar'], })} </Merits> ); }
When to use block overrides
Default defaultBlockOrder is header, content, actionBar. The root render maps those three blocks in order. Prepend banners (for example promo) or replace content when you need a custom tile layout while keeping items on the root component.
Important Props
Core Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
subtitle | ReactNode | Yes | — | Supporting header line (Merits.Header, primary color) |
meritsTitle | ReactNode | Yes | — | Main heading in Merits.Header |
items | { imageUrl: string; text: ReactNode; subtext: ReactNode }[] | Yes | — | Merit tiles mapped in Merits.Content |
buttonText | string | Yes | — | Label on the default CTA button in Merits.ActionBar |
buttonHref | string | Yes | — | href passed to the default Merits.ActionBar.Button |
Layout and Composition Props
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
children | ReactNode | function | No | undefined | Compound sub-components (Merits.Header, Merits.Content, Merits.ActionBar) or a function ({ defaultBlocks, defaultBlockOrder }) => ({ blocks, blockOrder }) to inject or reorder layout blocks |
className | string | No | undefined | Class name on the root stack (nbb-merits) |
sx | SxProps | No | undefined | MUI system styles for the root stack |
spacing | StackProps['spacing'] | No | { xs: 4, sm: 6 } | Vertical spacing on the root stack |
Merits inherits StackProps (except children). Default defaultBlockOrder: header, content, actionBar.
Sub-component props
Sub-components read from context and accept the same content keys as props to override locally.
| Sub-component | Main Props | Inherits | Built on |
|---|---|---|---|
Merits.Header | children, className, sx | StackProps | Stack + Typography |
Merits.Content | items, children, className, sx | StackProps | Stack + Merits.Content.MeritImage + Merits.Content.MeritText |
Merits.Content.MeritImage | meritSrc, meritAlt (default merit image), className, sx | BoxProps | Box (component="img") |
Merits.Content.MeritText | children, className, sx | TypographyProps | Typography |
Merits.ActionBar | buttonText, buttonHref, children, className, sx | StackProps | Stack + Merits.ActionBar.Button |
Merits.ActionBar.Button | children, href, className, sx | ButtonProps | Button (variant="contained", size="large") |
Default UI Blocks
| Block | Built on | Notes |
|---|---|---|
Merits (root) | Stack | Column layout, responsive spacing (nbb-merits) |
header (Merits.Header) | Stack + Typography | Centered subtitle (responsive h6/h4) and title (responsive up to 45px) |
content (Merits.Content) | Stack + Divider | Row on sm+; each item: image frame, text (MeritText), subtext (MeritSubtext, body1) |
actionBar (Merits.ActionBar) | Stack + Button | Contained CTA with buttonHref and buttonText; full width on xs |
Default root render order: header → content → actionBar.
TypeScript
import * as React from 'react';
import { Merits } from '@nodeblocks/frontend-merits-block';
import type { ReactNode } from 'react';
type MeritItem = {
imageUrl: string;
text: ReactNode;
subtext: ReactNode;
};
type MeritsSectionProps = {
subtitle: ReactNode;
meritsTitle: ReactNode;
buttonText: string;
buttonHref: string;
items: MeritItem[];
};
export function MeritsSection({
subtitle,
meritsTitle,
buttonText,
buttonHref,
items,
}: MeritsSectionProps) {
return (
<Merits
subtitle={subtitle}
meritsTitle={meritsTitle}
buttonText={buttonText}
buttonHref={buttonHref}
items={items}
/>
);
}