40+ landing page components for ReactBrowse now

Guides

Remix Setup

Integrate trink-ui into your Remix application with server-side rendering support.

Prerequisites

  • Remix v2 or React Router v7 (Remix successor)
  • React 18 or 19
  • Tailwind CSS v4
  • Node.js 18+

1Install the package

pnpm
pnpm add @trinkui/react
npm
npm install @trinkui/react

2Configure Remix

Add @trinkui/react to serverDependenciesToBundle so Remix can process the ESM package on the server:

remix.config.js (Remix v2)
/** @type {import("@remix-run/dev").AppConfig} */
export default {
  serverDependenciesToBundle: [
    "@trinkui/react",
    "@trinkui/shared",
  ],
};

If you are using React Router v7 (the Remix successor), no special config is needed since it uses Vite under the hood and handles ESM natively.

3CSS setup in root.tsx

Import your global CSS file containing the trink-ui variables in your root route. The CSS file should include the Tailwind source directive and all theme variables:

app/root.tsx
import type { LinksFunction } from "@remix-run/node";
import {
  Links,
  Meta,
  Outlet,
  Scripts,
  ScrollRestoration,
} from "@remix-run/react";
import styles from "./styles/globals.css?url";

export const links: LinksFunction = () => [
  { rel: "stylesheet", href: styles },
];

export default function App() {
  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <Links />
      </head>
      <body>
        <Outlet />
        <ScrollRestoration />
        <Scripts />
      </body>
    </html>
  );
}
app/styles/globals.css
@import "tailwindcss";

@source "node_modules/@trinkui/react/src";

:root {
  --trinkui-bg: 255 255 255;
  --trinkui-fg: 10 10 10;
  --trinkui-primary: 99 102 241;
  --trinkui-primary-fg: 255 255 255;
  --trinkui-secondary: 241 245 249;
  --trinkui-secondary-fg: 15 23 42;
  --trinkui-accent: 249 115 22;
  --trinkui-muted: 100 116 139;
  --trinkui-border: 226 232 240;
  --trinkui-surface: 248 250 252;
}

.dark {
  --trinkui-bg: 9 9 11;
  --trinkui-fg: 250 250 250;
  --trinkui-primary: 129 140 248;
  --trinkui-primary-fg: 255 255 255;
  --trinkui-secondary: 24 24 27;
  --trinkui-secondary-fg: 228 228 231;
  --trinkui-accent: 251 146 60;
  --trinkui-muted: 113 113 122;
  --trinkui-border: 39 39 42;
  --trinkui-surface: 18 18 21;
}

4Use components in a route

Import trink-ui sections directly in your Remix routes. Remix renders on the server first, and animations hydrate on the client:

app/routes/_index.tsx
import {
  HeroCentered,
  FeaturesGrid,
  FAQAccordion,
  CTACentered,
  FooterColumns,
} from "@trinkui/react";

export default function Index() {
  return (
    <main>
      <HeroCentered
        title="Welcome to our platform"
        subtitle="The modern way to build and ship."
        primaryAction={{ label: "Sign Up Free", href: "/signup" }}
        secondaryAction={{ label: "Documentation", href: "/docs" }}
      />
      <FeaturesGrid
        title="Built for developers"
        features={[
          { icon: "server", title: "Edge-Ready", description: "Deploy anywhere" },
          { icon: "lock", title: "Secure", description: "Enterprise-grade security" },
          { icon: "refresh", title: "Real-Time", description: "Instant updates" },
        ]}
      />
      <FAQAccordion
        title="Frequently asked questions"
        items={[
          { question: "Is it free?", answer: "Yes, the starter plan is free forever." },
          { question: "Can I self-host?", answer: "Absolutely. Full Docker support included." },
        ]}
      />
      <CTACentered
        theme="dark"
        title="Ready to ship?"
        primaryAction={{ label: "Get Started", href: "/signup" }}
      />
      <FooterColumns
        brand={{ name: "Remix App", description: "Built with trink-ui" }}
        columns={[
          { title: "Product", links: [{ label: "Features", href: "/features" }] },
          { title: "Legal", links: [{ label: "Privacy", href: "/privacy" }] },
        ]}
      />
    </main>
  );
}

Server-side rendering notes

trink-ui components render static HTML on the server and hydrate animations on the client. The animation wrappers use useReducedMotion to respect user preferences. If you want to skip client-side hydration entirely for a section, pass animated={false}:

<HeroCentered
  animated={false}
  title="No client JS needed"
  subtitle="Fully server-rendered."
  primaryAction={{ label: "Learn More", href: "/docs" }}
/>

Next step

Learn how to implement dark mode with a toggle and per-section theming.

Dark Mode Guide