Skip to content

Overview & Component Creation

Components in NETWRIX are Astro files located in src/components/ organized by type (card, hero, cta, etc.). In this section, you’ll build CardTest - a simple card component with media, title, and subtitle using custom NETWRIX components.


Create the file: src/components/card/CardTest.astro

---
import { Asset } from "@terrahq/astro-core";
import Btn from "@components/btn/Btn.astro";
const {
payload: {
customClass,
title,
subtitle,
media,
link,
language
}
} = Astro.props;
---
<Btn
language={language}
payload={{
button: {
...link,
},
customContent: true,
customClass: ["c--card-test", customClass].filter(Boolean).join(" "),
}}
>
<div class="c--card-test__media-wrapper">
<Asset
payload={{
...media,
decodingAsync: media?.decoding,
customClass: "c--card-test__media-wrapper__media",
sizes: "80px",
}}
/>
</div>
<div class="c--card-test__wrapper">
{title &&
<h2 class="c--card-test__wrapper__title">{title}</h2>
}
{subtitle &&
<p class="c--card-test__wrapper__subtitle">{subtitle}</p>
}
</div>
</Btn>

---
import { Asset } from "@terrahq/astro-core";
import Btn from "@components/btn/Btn.astro";
const { payload: { customClass, title, subtitle, media, link, language } } = Astro.props;
---

Key Imports:

  • Asset: Terra’s component for handling images and media with responsive optimization
  • Btn: NETWRIX’s button wrapper that handles links, buttons, and clickable containers

Props Destructuring:

We extract props from Astro.props.payload - this is the standard pattern for NETWRIX components. The payload contains all the component’s configuration data.


The component follows BEM (Block Element Modifier) naming convention:

c--card-test ← Block
├── c--card-test__media-wrapper ← Element
│ └── c--card-test__media-wrapper__media ← Sub-element
└── c--card-test__wrapper ← Element
├── c--card-test__wrapper__title ← Sub-element
└── c--card-test__wrapper__subtitle ← Sub-element

Class Naming Pattern:

  • c-- prefix indicates Component
  • card-test is the component name
  • __ separates block from element
  • -- separates block/element from modifier (e.g., c--card-test--dark)

The <Btn> component wrapper provides essential functionality:

Consistent link/button behaviorAccessibility features (ARIA attributes)Keyboard navigation supportProper semantic HTML (<a> vs <button>)Modal and anchor functionality

By using customContent: true, we can pass custom HTML as children instead of just a text label, making the entire card clickable.

<Btn
payload={{
button: { ...link },
customContent: true, // ← Enables custom HTML content
}}
>
<!-- Your custom card structure here -->
</Btn>

<Asset
payload={{
...media,
decodingAsync: media?.decoding,
customClass: "c--card-test__media-wrapper__media",
sizes: "80px",
}}
/>

The Asset component handles:

  • ✨ Responsive images with srcset
  • 🚀 Lazy loading for performance
  • ♿ Proper alt attributes for accessibility
  • 🖼️ WebP format with fallbacks

Important Parameters:

  • ...media - Spreads all media properties from CMS (url, alt, type, etc.)
  • decodingAsync - Tells browser to decode image asynchronously (better performance)
  • customClass - Your custom CSS class for styling
  • sizes - Tells browser the display size for optimal image selection

1. Always destructure from payload

const { payload: { title, subtitle } } = Astro.props;

2. Use conditional rendering for optional props

{title && <h2>{title}</h2>}
{subtitle && <p>{subtitle}</p>}

3. Filter empty classes when combining

customClass: ["c--card-test", customClass].filter(Boolean).join(" ")

4. Follow BEM naming strictly

class="c--card-test__wrapper__title"

5. Spread media props to preserve all properties

<Asset payload={{ ...media, customClass: "..." }} />

1. Don’t hardcode values - use props

<!-- ❌ Bad -->
<h2>Welcome</h2>
<!-- ✅ Good -->
{title && <h2>{title}</h2>}

2. Don’t skip conditional checks for optional props

<!-- ❌ Bad - Will show empty <h2> if no title -->
<h2>{title}</h2>
<!-- ✅ Good -->
{title && <h2>{title}</h2>}

3. Don’t mix naming conventions

<!-- ❌ Bad -->
class="card-test_wrapper-title"
<!-- ✅ Good -->
class="c--card-test__wrapper__title"

4. Don’t forget to spread media props

<!-- ❌ Bad - Loses alt, type, and other properties -->
<Asset payload={{ url: media.url }} />
<!-- ✅ Good -->
<Asset payload={{ ...media, customClass: "..." }} />

Understanding the expected props structure:

interface CardTestProps {
payload: {
customClass?: string; // Optional additional CSS class
title: string; // Card title
subtitle?: string; // Optional card subtitle
language: string; // Current language (e.g., "en")
media: { // Media object
type: "Image";
url: string;
alt: string;
decoding?: boolean;
};
link: { // Button/link configuration
label: string;
url: string;
target: "_self" | "_blank";
option?: string;
};
};
}

Btn (src/components/btn/Btn.astro) is a custom wrapper around Terra’s ButtonLink that extends its functionality to handle links, modals, and anchors. It supports custom icons (arrow-right, arrow-left, arrow-top, play, download) and enables wrapping custom HTML content by setting customContent: true, which is essential for making entire cards clickable. To understand how it works, review the ButtonLink documentation in Astro Core.

Asset (from @terrahq/astro-core) is Terra’s image optimization component that automatically handles responsive images with srcset, WebP format conversion, lazy loading, and proper accessibility attributes. The key parameters are decodingAsync for better performance, customClass for styling, and sizes to help the browser select the optimal image. For complete details, check the Asset Component documentation.


Here’s how you would use the CardTest component in a module or page:

<CardTest
payload={{
language: "en",
title: "Cloud Security",
subtitle: "Enterprise-grade security solutions for your data",
customClass: "my-custom-modifier",
media: {
type: "Image",
url: "/images/security-icon.webp",
alt: "Cloud Security Icon",
decoding: true
},
link: {
label: "Learn More",
url: "/services/cloud-security",
target: "_self"
}
}}
/>

Now that you’ve created your component structure, it’s time to add styling!

👉 Continue to Styling with CSS & SCSS to learn how to style your component using the NETWRIX design system.