Feature Rows
A free alternating feature section with scroll reveals for React and Next.js.
Ship with Relay
Everything between git push and production
Relay turns a commit into a deploy, a branch into a shareable preview, and production into something you can actually see.
Ship checkout redesign
Maya Okafor pushed 2 minutes ago
Deploys
Push to production in under a minute
Every commit builds, runs your checks and ships to the edge automatically. Roll back to any previous release with one click, no redeploy needed.
- 49s median from push to live
- Zero-downtime releases by default
Checkout redesign
Migrate search to the edge runtime
Fix rounding in invoice totals
Seeded from last night's production snapshot
Previews
A URL for every branch
Each pull request gets its own isolated preview environment, seeded with production-shaped data and torn down on merge.
- Updated on every push to the branch
- Password-protected for outside reviewers
db.cart_items regressed
c81d4e0284ms against a 41ms baseline, first seen in this deploy. Jump to cart/pricing.ts:142.
Observability
See every request end to end
Traces, logs and metrics are captured out of the box and linked to the deploy that produced them, so you can jump from a slow span straight to the code behind it.
- P95 regressions flagged within minutes
- 30 days of retention on every plan
Feature Rows is a free feature section React component for Next.js, built with Motion and styled with Tailwind. It lays out two to six rows of media and copy on an alternating two column grid, and reveals each row on its own trigger as it scrolls into view: the copy rises in first and the media slot follows a tenth of a second later.
You bring the rows as ordinary children marked with data-feature-row, data-feature-media and data-feature-copy; the component owns the layout and the reveal choreography. Even rows swap column order visually with CSS order only, so the DOM, reading and Tab order stay identical in every row. The authored HTML is the final visible state, which means crawlers, no-JS visitors and reduced-motion users always see every row immediately.
Install
Add the @te registry to your components.json once:
{
"registries": {
"@te": "https://templateempire.io/r/{name}.json"
}
}Then install the component:
npx shadcn@latest add @te/feat-rowsUsage
import { FeatureRows } from "@/components/feat-rows";
export default function Page() {
return (
<FeatureRows>
<div data-feature-row>
<div data-feature-media>
<img src="/screens/deploys.png" alt="Deploy dashboard" />
</div>
<div data-feature-copy>
<h3>Push to production in under a minute</h3>
<p>Every commit builds, ships to the edge and can be rolled back.</p>
</div>
</div>
<div data-feature-row>
<div data-feature-media>
<img src="/screens/previews.png" alt="Preview environments" />
</div>
<div data-feature-copy>
<h3>A URL for every branch</h3>
<p>Each pull request gets its own isolated preview environment.</p>
</div>
</div>
</FeatureRows>
);
}Props
| Prop | Type | Default | Description |
|---|---|---|---|
| children | ReactNode | required | 2 to 6 row wrappers marked data-feature-row, each containing a data-feature-media slot and a data-feature-copy slot. Author the media slot first in every row. |
| className | string | undefined | Additional classes merged onto the section root. |
Customisation
Feature Rows declares no tokens of its own; it is a layout and reveal frame that consumes the global semantic tokens through the Tailwind utilities on its root, and your row markup owns the card and media styling. Override spacing from the className prop, and retheme your rows through the same semantic tokens the rest of the library uses:
<FeatureRows className="gap-24 md:gap-32">
{rows}
</FeatureRows>
/* rows styled with semantic tokens */
:root {
--border: oklch(from var(--color-neutral-50) l c h / 0.16);
}Frequently asked questions
- Is Feature Rows free for commercial use?
- Yes. Feature Rows is MIT licensed, so you can use it in personal and commercial projects without attribution.
- Does Feature Rows affect performance or accessibility?
- The reveal animates transform and opacity only, inside space each row has already reserved, so layout shift is zero. Each row reveals on its own trigger and runs once. Rows waiting below the fold are set to their starting state before the page first paints, so no row is ever seen visible and then hidden; they fade with opacity only and stay in the accessibility tree, and if focus lands in a row before its reveal, that row comes to rest at once so a focused link is never transparent. The alternation is CSS order only, keeping DOM, reading and Tab order identical in every row, the two column layout follows the section's own width rather than the viewport's, and with reduced motion enabled every row renders immediately with nothing animated or observed.
- How do I control which side the media appears on?
- Author the media slot first in every row. Odd rows show media on the left and copy on the right from the md breakpoint up, and even rows swap visually via CSS order, so the alternation is automatic. On mobile every row is a single column with media on top. To change the rhythm, reorder the rows themselves; the component never changes the DOM order inside a row.