Getting Started
Remix
Everything you need to set up Tremor with Remix.
Installation
Tremor is designed for React and requires React v18.2.0+
- In our terminal, we create a new Remix project. We use git and will be installing via npm.
npx create-remix@latest my-project && cd my-project
npm install -D tailwindcss postcss autoprefixernpx tailwindcss init --ts -p
Create a ./app/tailwind.css file and add the @tailwind directives.@tailwind base;@tailwind components;@tailwind utilities;
Import the tailwind.css file in your ./app/root.tsx file like so:
import type { LinksFunction } from "@remix-run/node";import stylesheet from "~/tailwind.css?url"; export const links: LinksFunction = () => [ { rel: "stylesheet", href: stylesheet },];
To install, run:
npm install -D @tailwindcss/forms
In order for the animations to be applied correctly, we extend thetailwind.config.ts. We also add the @tailwindcss/forms plugin. Note that we also added the paths to the template files in the content section.
import type { Config } from "tailwindcss" // (Optional) Import default theme when using a custom font (Step 7)//import defaultTheme from 'tailwindcss/defaultTheme'; export default { content: ["./app/**/*.{js,jsx,ts,tsx}"], theme: { extend: { keyframes: { hide: { from: { opacity: "1" }, to: { opacity: "0" }, }, slideDownAndFade: { from: { opacity: "0", transform: "translateY(-6px)" }, to: { opacity: "1", transform: "translateY(0)" }, }, slideLeftAndFade: { from: { opacity: "0", transform: "translateX(6px)" }, to: { opacity: "1", transform: "translateX(0)" }, }, slideUpAndFade: { from: { opacity: "0", transform: "translateY(6px)" }, to: { opacity: "1", transform: "translateY(0)" }, }, slideRightAndFade: { from: { opacity: "0", transform: "translateX(-6px)" }, to: { opacity: "1", transform: "translateX(0)" }, }, accordionOpen: { from: { height: "0px" }, to: { height: "var(--radix-accordion-content-height)" }, }, accordionClose: { from: { height: "var(--radix-accordion-content-height)", }, to: { height: "0px" }, }, dialogOverlayShow: { from: { opacity: "0" }, to: { opacity: "1" }, }, dialogContentShow: { from: { opacity: "0", transform: "translate(-50%, -45%) scale(0.95)", }, to: { opacity: "1", transform: "translate(-50%, -50%) scale(1)" }, }, drawerSlideLeftAndFade: { from: { opacity: "0", transform: "translateX(100%)" }, to: { opacity: "1", transform: "translateX(0)" }, }, drawerSlideRightAndFade: { from: { opacity: "1", transform: "translateX(0)" }, to: { opacity: "0", transform: "translateX(100%)" }, }, }, animation: { hide: "hide 150ms cubic-bezier(0.16, 1, 0.3, 1)", slideDownAndFade: "slideDownAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", slideLeftAndFade: "slideLeftAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", slideUpAndFade: "slideUpAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", slideRightAndFade: "slideRightAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", // Accordion accordionOpen: "accordionOpen 150ms cubic-bezier(0.87, 0, 0.13, 1)", accordionClose: "accordionClose 150ms cubic-bezier(0.87, 0, 0.13, 1)", // Dialog dialogOverlayShow: "dialogOverlayShow 150ms cubic-bezier(0.16, 1, 0.3, 1)", dialogContentShow: "dialogContentShow 150ms cubic-bezier(0.16, 1, 0.3, 1)", // Drawer drawerSlideLeftAndFade: "drawerSlideLeftAndFade 150ms cubic-bezier(0.16, 1, 0.3, 1)", drawerSlideRightAndFade: "drawerSlideRightAndFade 150ms ease-in", }, }, }, plugins: [require("@tailwindcss/forms")],} satisfies Config
To install, run:
npm install tailwind-variants clsx tailwind-merge @remixicon/react
(Optional) If you plan to use all components, you can add all dependencies here:
npm install @radix-ui/react-accordion @radix-ui/react-checkbox @radix-ui/react-dialog @radix-ui/react-dropdown-menu @radix-ui/react-hover-card @radix-ui/react-label @radix-ui/react-navigation-menu @radix-ui/react-popover @radix-ui/react-radio-group @radix-ui/react-select @radix-ui/react-slider @radix-ui/react-slot @radix-ui/react-switch @radix-ui/react-tabs @radix-ui/react-toast @radix-ui/react-tooltip @internationalized/date date-fns@3.6.0 react-day-picker@8.10.1 recharts @react-aria/datepicker @react-stately/datepicker
In all our examples, we use Geist Font. This is not required, use any other font you like.
Download the latest Geist.zip here.
Create a new fonts folder in your public directory. Add the variable font file from the downloaded folder.
.├── public│ └── fonts│ │ └── GeistVariableVF.woff2...
Then in the tailwind.css, add the font face:
@font-face { font-family: 'Geist Sans'; src: url('/fonts/GeistVariableVF.woff2') format('woff2'); font-style: normal; font-weight: 100 900;}
Lastly, extend your theme with the font in your tailwind.conifg.ts. Make sure that you are also importing the default theme.
import type { Config } from 'tailwindcss' // (Optional) Import default theme when using a custom font (Step 7)import defaultTheme from 'tailwindcss/defaultTheme'; export default { content: ['./app/**/*.{js,jsx,ts,tsx}'], theme: { extend: { fontFamily: { sans: ['Geist Sans', ...defaultTheme.fontFamily.sans], }, keyframes: {//...
To top if off, we recommend adding antialiasing and a default darkmode background to your root.tsx
<html lang="en" className="antialiased dark:bg-gray-950">
Our components depend on a few utilities. You can read more about them in the Utilities section.
Create a new lib folder in /app. Add a new utils.ts file inside. Paste the following utilities into this file.
// Tremor Raw cx [v0.0.0] import clsx, { type ClassValue } from "clsx"import { twMerge } from "tailwind-merge" export function cx(...args: ClassValue[]) { return twMerge(clsx(...args))} // Tremor Raw focusInput [v0.0.1] export const focusInput = [ // base "focus:ring-2", // ring color "focus:ring-blue-200 focus:dark:ring-blue-700/30", // border color "focus:border-blue-500 focus:dark:border-blue-700",] // Tremor Raw focusRing [v0.0.1] export const focusRing = [ // base "outline outline-offset-2 outline-0 focus-visible:outline-2", // outline color "outline-blue-500 dark:outline-blue-500",] // Tremor Raw hasErrorInput [v0.0.1] export const hasErrorInput = [ // base "ring-2", // border color "border-red-500 dark:border-red-700", // ring color "ring-red-200 dark:ring-red-700/30",]
Next, we add the chart utilities. Add a new chartUtils.ts file and paste the following code into this file.
// Tremor Raw chartColors [v0.1.0] export type ColorUtility = "bg" | "stroke" | "fill" | "text" export const chartColors = { blue: { bg: "bg-blue-500", stroke: "stroke-blue-500", fill: "fill-blue-500", text: "text-blue-500", }, emerald: { bg: "bg-emerald-500", stroke: "stroke-emerald-500", fill: "fill-emerald-500", text: "text-emerald-500", }, violet: { bg: "bg-violet-500", stroke: "stroke-violet-500", fill: "fill-violet-500", text: "text-violet-500", }, amber: { bg: "bg-amber-500", stroke: "stroke-amber-500", fill: "fill-amber-500", text: "text-amber-500", }, gray: { bg: "bg-gray-500", stroke: "stroke-gray-500", fill: "fill-gray-500", text: "text-gray-500", }, cyan: { bg: "bg-cyan-500", stroke: "stroke-cyan-500", fill: "fill-cyan-500", text: "text-cyan-500", }, pink: { bg: "bg-pink-500", stroke: "stroke-pink-500", fill: "fill-pink-500", text: "text-pink-500", }, lime: { bg: "bg-lime-500", stroke: "stroke-lime-500", fill: "fill-lime-500", text: "text-lime-500", }, fuchsia: { bg: "bg-fuchsia-500", stroke: "stroke-fuchsia-500", fill: "fill-fuchsia-500", text: "text-fuchsia-500", },} as const satisfies { [color: string]: { [key in ColorUtility]: string }} export type AvailableChartColorsKeys = keyof typeof chartColors export const AvailableChartColors: AvailableChartColorsKeys[] = Object.keys( chartColors,) as Array<AvailableChartColorsKeys> export const constructCategoryColors = ( categories: string[], colors: AvailableChartColorsKeys[],): Map<string, AvailableChartColorsKeys> => { const categoryColors = new Map<string, AvailableChartColorsKeys>() categories.forEach((category, index) => { categoryColors.set(category, colors[index % colors.length]) }) return categoryColors} export const getColorClassName = ( color: AvailableChartColorsKeys, type: ColorUtility,): string => { const fallbackColor = { bg: "bg-gray-500", stroke: "stroke-gray-500", fill: "fill-gray-500", text: "text-gray-500", } return chartColors[color]?.[type] ?? fallbackColor[type]} // Tremor Raw getYAxisDomain [v0.0.0] export const getYAxisDomain = ( autoMinValue: boolean, minValue: number | undefined, maxValue: number | undefined,) => { const minDomain = autoMinValue ? "auto" : minValue ?? 0 const maxDomain = maxValue ?? "auto" return [minDomain, maxDomain]} // Tremor Raw hasOnlyOneValueForKey [v0.1.0] export function hasOnlyOneValueForKey( array: any[], keyToCheck: string,): boolean { const val: any[] = [] for (const obj of array) { if (Object.prototype.hasOwnProperty.call(obj, keyToCheck)) { val.push(obj[keyToCheck]) if (val.length > 1) { return false } } } return true}
When adding components, we recommend adding them to a components directory inside /app. Here is an example on how we'd structure our app:
.├── app│ ├── components│ │ ├── Accordion.tsx│ │ ├── Badge.tsx│ │ └── ...│ ├── entry.client.tsx│ ├── entry.server.tsx│ ├── lib│ ├── ├── chartUtils.tsx│ │ └── utils.ts│ ├── root.tsx│ ├── routes│ │ └── _index.tsx│ └── tailwind.css├── build├── package-lock.json├── package.json├── postcss.config.js├── public│ ├── fonts│ └── ...├── tailwind.config.ts├── tsconfig.json└── vite.config.ts
Dark mode usage:
For all examples, we use bg-gray-950 as the overall background color. You can add this to your <html class="dark:bg-gray-950"> tag.
Font smoothing (antialiasing):
On our website, we apply font smoothing and recommend you do the same. Simply add the antialiased utility to the HTML tag <html className="antialiased">.
Warning: 'className' is missing in props validation:
Add this inside your .eslintrc.cjs: extends: ["@remix-run/eslint-config", "@remix-run/eslint-config/node"]