New: the Halo × Pennylane integration by Halentra is live. Take a look
Halentra
Home/Blog/MDX components

Every block you can drop into an article

The MDX component set for Halentra's long-form content. Copy a block, fill in the content, keep the rhythm.

01 · Prose + link

Body copy

Default paragraph, bold and link styling, applied automatically to every MDX document through the shared Prose wrapper.

Body copy runs on a comfortable line height so long explanations stay easy to follow. Inline emphasis stays semantic and quiet, and a link looks like this one, styled through the shared text-link component rather than one-off inline styles.

usage.mdx
Body copy runs as plain Markdown. Use **bold** for
semantic emphasis and [a link](/demo) for anything clickable.
02 · Callouts

Callout

Two tones are wired up today: a soft mint box for general notes, and a dark panel for anything readers must not skim past.

Note

Rates are set per user, per month. Devices sit on their own line so growth stays visible.

Heads up

Never write “unlimited” into a contract. It is not a commercial term, it is a bet you lose on the worst possible week.

usage.mdx
<Callout label="Note" tone="info">
  Rates are set per user, per month.
</Callout>

<Callout label="Heads up" tone="danger">
  Never write "unlimited" into a contract.
</Callout>
PropType
label
required
string
tone
optional
"info" | "danger" | "question"default "info"
children
required
ReactNode
03 · CTA

Ready to see it on your own data?

30 minutes, no slides.

usage.mdx
<Cta
  title="Ready to see it on your own data?"
  subtitle="30 minutes, no slides."
  primaryLabel="Book a demo"
  primaryHref="/demo"
  secondaryLabel="See pricing"
  secondaryHref="/solutions"
/>
PropType
title
required
string
subtitle
optional
string
primaryLabel
required
string
primaryHref
required
string
secondaryLabel
optional
string
secondaryHref
optional
string

The secondary button only renders when both secondaryLabel and secondaryHref are set.

04 · Image

Inline image

Renders full width by default, opens a full-screen lightbox on click, and preloads on hover so the transition feels instant.

Caption stays quiet and one line where possible.
usage.mdx
<MediaImage
  src="/assets/hudu/hero.webp"
  alt="Example product screenshot"
  maxWidth={360}
/>
PropType
src
required
string
alt
optional
string
maxWidth
optional
number

Caps the rendered width in px. Full width when unset.

05 · Quote

Pull quote

A discount is a price change. An exception is a scope change. Confusing the two is how margins disappear quietly.

- Marc Aubert, Halentra
usage.mdx
<PullQuote
  text="A discount is a price change."
  name="Marc Aubert"
  role="Halentra"
/>
PropType
text
required
string
name
required
string
role
required
string
06 · Testimonial

Customer quote

We stopped rebuilding quotes in a spreadsheet. Three tiers went into the PSA and renewals took half the time they used to.

Sarah Lindqvist
Operations lead, Northbound IT
usage.mdx
<Quote
  text="We stopped rebuilding quotes in a spreadsheet."
  name="Sarah Lindqvist"
  role="Operations lead, Northbound IT"
/>
PropType
text
required
string
name
required
string
role
required
string
07 · Table

Data table

TierCoverageWhat it includes
EssentialBusiness hoursPatching, monitoring, service desk.
StandardExtendedProject hours, quarterly reviews, a named engineer.
Critical24/7Out-of-hours response and a tested recovery plan.
usage.mdx
<DataTable
  columns={["Tier", "Coverage", "What it includes"]}
  rows={[
    ["Essential", "Business hours", "Patching, monitoring, service desk."],
  ]}
/>
PropType
columns
required
string[]
widths
optional
string[]

CSS grid track sizes, one per column. Defaults to an equal 1fr per column.

rows
required
ReactNode[][]
08 · FAQ

Frequently asked

Yes. Every block below is a standalone MDX component, so an article can move from prose to a callout to a table without any special wiring.

Use the closest available block, or ask engineering to add it. The gaps section below tracks what the design calls for but the site doesn't render yet.

usage.mdx
<Faq
  items={[
    { q: "Can I mix components?", a: "Yes, freely." },
  ]}
/>
PropType
items
required
{ q: string; a: string }[]
09 · Key numbers

Key numbers

14
components live today
9
still on the wishlist
1
place they're all documented
usage.mdx
<KeyNumbers
  items={[
    { value: "12", label: "components live today" },
  ]}
/>
PropType
items
required
{ value: string; unit?: string; label: string }[]
10 · Timeline

Timeline

Step 1
Write the draft

Plain prose first, blocks added only where they earn their place.

Step 2
Drop in the blocks

Callouts for asides, a table for anything tabular, a quote where it's earned.

Step 3
Publish

The Prose wrapper keeps typography consistent across every collection.

usage.mdx
<Timeline
  steps={[
    { period: "Step 1", title: "Write the draft", desc: "Plain prose first." },
  ]}
/>
PropType
steps
required
{ period: string; title: string; desc: string }[]
11 · Pull highlight

Pull highlight

Clients forgive increases. They do not forgive surprises.

usage.mdx
<StatementBanner
  text="Clients forgive increases. They do not forgive surprises."
  accent="surprises"
/>
PropType
text
required
string
accent
required
string

Must be an exact substring of text. That substring is what gets the accent colour.

13 · Pros and cons

Pros and cons

A two-column card: a green checkmarked list of what works well next to a plain list of what to watch out for.

Works well
  • Quotes leave the office in minutes.
  • Renewals stop being a negotiation from scratch.
Watch out
  • Legacy contracts need a migration window.
  • Sales needs one training session, not a memo.
usage.mdx
<ProsCons
  works={{
    label: "Works well",
    items: ["Quotes leave the office in minutes."],
  }}
  watch={{
    label: "Watch out",
    items: ["Legacy contracts need a migration window."],
  }}
/>
PropType
works
required
{ label: string; items: string[] }
watch
required
{ label: string; items: string[] }
14 · Download

Download

A file card with an icon, title, and format/size subtitle, next to a pill button that downloads the attached document.

Pricing model templateXLSX · 82 KB
Download
usage.mdx
<Download
  label="Pricing model template"
  url="/api/media/files/cms-body/pricing-model-template.xlsx"
  fileName="pricing-model-template.xlsx"
  size="83968"
/>
PropType
label
required
string
url
required
string
fileName
required
string
size
required
string

Stringified byte count - the subtitle formats it into a human-readable size.

15 · Tabs

Tabs

A row of labeled tabs; clicking a label swaps the single paragraph body shown below it.

A short summary of what this plan covers, in one paragraph.

usage.mdx
<Tabs
  tabs={[
    { label: "Overview", body: "A short summary tab body goes here." },
    { label: "Pricing", body: "A second tab, swapped in on click." },
    { label: "Support", body: "A third tab, same paragraph treatment." },
  ]}
/>
PropType
tabs
required
{ label: string; body: string }[]
17 · Before and after

Before and after

A draggable image-comparison slider - drag the handle, or focus it and use the arrow keys, to reveal more of the after image.

BEFOREAFTER
usage.mdx
<BeforeAfter
  before="/assets/hudu/work-screen-capture.webp"
  after="/assets/hudu/work-hudini-ai.webp"
  beforeLabel="BEFORE"
  afterLabel="AFTER"
/>
PropType
before
required
string

Image URL shown at split 0.

after
required
string

Image URL revealed as the split grows.

beforeLabel
optional
stringdefault "BEFORE"
afterLabel
optional
stringdefault "AFTER"
18 · Ask an expert

Ask an expert

A compact dark banner pairing a circular avatar photo with a title/subtitle line and a bright pill CTA - drop it after a pricing or comparison section to route the reader to a named contact.

Not sure where your contracts stand?
Talk to us
usage.mdx
<AskExpert
  photo="/assets/testimonials/amerix-livio.png"
  title="Not sure where your contracts stand?"
  body="Send us your current price list. We will tell you what to keep."
  ctaLabel="Talk to us"
  ctaUrl="/contact"
/>
PropType
photo
optional
string

Circular avatar photo URL. Renders a bare placeholder circle when unset.

title
required
string
body
required
string
ctaLabel
required
string
ctaUrl
required
string
23 · Feature comparison

Feature comparison

A two-competitor table with category group headers, five per-cell content types (checks, an "Advantage" pill, crosses, plain text, and a linked price), optional per-row info tooltips, and a footer CTA bar.

Feature
Hudu
Distributed by Halentra
IT Glue
Alternative
Documentation
IPAMIP address management, built in.
Advantage
Unlimited assets
Checklists & runbooks
Advantage
Basic checklists
Photos & diagrams
Advanced
API access
Self-hosted optionRun it on your own infrastructure instead of the vendor's cloud.
Advantage
Demo only
Automation
Radar add-on, €9 / company / month
AI search
Advantage
Pricing and value
Included users
Unlimited users
5 users on standard plans
Starting priceBilled annually; monthly billing is available at a small premium.
€44 / user / month
Contract length
Monthly, no lock-in
Annual commitment
Based on public pricing pages, checked June 2026. Spotted something out of date?
Plan a migration
usage.mdx
<FeatureComparison
  columns={[
    { name: "Hudu", subtitle: "Distributed by Halentra" },
    { name: "IT Glue", subtitle: "Alternative" },
  ]}
  groups={[
    {
      label: "Documentation",
      rows: [
        {
          feature: "IPAM",
          tooltip: "IP address management, built in.",
          cells: [{ kind: "check" }, { kind: "cross" }],
        },
      ],
    },
  ]}
  footerNote="Based on public pricing pages, checked June 2026."
  footerLinkLabel="Spotted something out of date?"
  footerLinkUrl="/contact"
  ctaLabel="Plan a migration"
  ctaUrl="/contact"
/>
PropType
columns
required
[{ name: string; subtitle: string; logo?: string }, { name: string; subtitle: string; logo?: string }]

Exactly two entries. The first is the highlighted column - Halentra's own pick.

groups
required
{ label: string; rows: { feature: string; tooltip?: string; cells: [Cell, Cell] }[] }[]

Cell is one of { kind: "check" }, { kind: "check-advantage" }, { kind: "cross" }, { kind: "text"; text }, or { kind: "link"; text; href }.

footerNote
required
string
footerLinkLabel
optional
string
footerLinkUrl
optional
string

The inline footer link only renders when both footerLinkLabel and footerLinkUrl are set.

ctaLabel
required
string
ctaUrl
required
string
19 · Not built yet

On the wishlist

The design references these blocks too, but nothing renders them here yet. Flagging them so the gap is visible rather than silently missing.

  • Image beside text
    A screenshot with one paragraph of context, mirrored left/right down a long article.
  • Video embed
    A poster image with a play affordance over it.
  • Comparison table
    A dedicated tiered layout with checkmarks, distinct from the plain data table.
  • Highlighted list
    A bullet list styled with arrow markers for callable-out takeaways.
  • Question callout
    A third callout tone, alongside note and heads up.