Custom Alias
When you create a new project with Remix, the default template installs the vite-tsconfig-paths dependency so there is no need to configure it manually. If you want to configure it without the dependency, follow these steps:
- Install the types of Node.js:
pnpm i @types/node -E -D- In your vite.config.tsfile, add the following:
import { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'app') // 👈 /app or /src, your app directory.
    }
  },
  plugins: [
    remix({
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true
      }
    })
  ]
});- In your tsconfig.jsonfile, add the following:
{
  "compilerOptions": {
    // 👀 your other config...
    "baseUrl": ".",
    "paths": {
      "@/": ["./app/*"] // 👈 /app or /src, your app directory.
    }
  }
}💡 If you’re using VSCode, I recommend restart the editor to apply the changes.
Import any CSS styles
If you’re using Remix with Vite, you can import styles like this:
import type { LinksFunction } from "@remix-run/node";
import stylesheet from "@/styles/globals.css?url"; // 👈
export const links: LinksFunction = () => [
  { rel: "stylesheet", href: stylesheet },
];and that the Vite compiler does not interpret .css files as paths:
export default defineConfig({
  plugins: [
    remix({
      ignoredRouteFiles: ['**/*.css'] // 👈
    })
  ]
});Routes
All your routes should be inside the app/routes folder:
📂 app
  📂 routes| 📂 | File | Description | 
|---|---|---|
| ⚛️ | app.tsx | Layout that wraps all the paths of /app. You need to add <Outlet />from @remix-run/react. | 
| 🟦 | action.name.ts | Route /action/name. Exports a server-side action. | 
| ⚛️ | app._index.tsx | /app (main page) | 
| ⚛️ | app.settings.tsx | /app/settings | 
| ⚛️ | app.$username.tsx | /app/pheralb or /app/midudev. To obtain the $username: loader#params | 
| ⚛️ | root.tsx | Layout that wraps the entire application. | 
How to use Remix on Vercel
- Install @vercel/remixpackage:
pnpm i @vercel/remix -E- Add Vercel Vite Preset to your vite.config.ts:
import { vitePlugin as remix } from '@remix-run/dev';
import { defineConfig } from 'vite';
// Plugins:
import tsconfigPaths from 'vite-tsconfig-paths';
import { vercelPreset } from '@vercel/remix/vite'; // 👈
export default defineConfig({
  plugins: [
    remix({
      presets: [vercelPreset()], // 👈
      future: {
        v3_fetcherPersist: true,
        v3_relativeSplatPath: true,
        v3_throwAbortReason: true
      }
    }),
    tsconfigPaths()
  ]
});- Replace all @remix-run/nodeimports with@vercel/remix. Example:
import type { MetaFunction } from '@vercel/remix';
export const meta: MetaFunction = () => {
  return [{ title: 'New Remix App' }, { name: 'description', content: 'Welcome to Remix!' }];
};- 💡 Check @vercel/remixdocumentation.
Install & configure Tailwind CSS with Prettier
- Install Tailwind CSS, Prettier & Autoprefixer:
pnpm i tailwindcss prettier prettier-plugin-tailwindcss autoprefixer -E -D- Create a global.cssorstyles.cssin theapp/folder:
@tailwind base;
@tailwind components;
@tailwind utilities;- Import the styles in the app/routes/root.tsx:
import type { LinksFunction } from "@remix-run/node";
import stylesheet from "@/styles/globals.css?url"; // or styles.css 👀
export const links: LinksFunction = () => [
  { rel: "stylesheet", href: stylesheet },
];- Create a tailwind.config.tsfile:
npx tailwindcss init --ts- In the tailwind.config.tsfile, add the following:
export default {
  content: ['./app/**/*.{js,jsx,ts,tsx}'], // 👈
  ...
} satisfies Config- Create a postcss.config.jsfile:
export default {
  plugins: {
    tailwindcss: {},
    autoprefixer: {}
  }
};- Create a prettier.config.jsfile:
module.exports = {
  plugins: ['prettier-plugin-tailwindcss'],
  ...
};Dark mode
💡 Here we wil use
remix-themeslibrary, but you can create all utilities and providers manually. Check remix/examples - dark mode repository.
- Install remix-themes and clsx:
pnpm i remix-themes clsx -E- Enable darkModeoption in thetailwind.config.tsfile:
const config = {
  darkMode: ['class']
  //...
} satisfies Config;- Create a session.server.tsxfile in /app folder with the following content:
import { createCookieSessionStorage } from '@remix-run/node'; //
import { createThemeSessionResolver } from 'remix-themes';
// You can default to 'development' if process.env.NODE_ENV is not set:
const isProduction = process.env.NODE_ENV === 'production';
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: 'website-theme', // 👈 Cookie name.
    path: '/',
    httpOnly: true,
    sameSite: 'lax',
    secrets: ['s3cr3t'],
    ...(isProduction ? { domain: 'my-website.com', secure: true } : {}) // 👈 Website URL.
  }
});
export const themeSessionResolver = createThemeSessionResolver(sessionStorage);- In app/routesfolder, create aaction.set-theme.tsfile with the following content:
import { createThemeAction } from 'remix-themes';
import { themeSessionResolver } from '@/sessions.server'; // 👈 Import your session.server.tsx.
export const action = createThemeAction(themeSessionResolver);- In app/routes/root.tsx…
- ☁️ Use the loader to get the theme from server-side:
import type { LoaderFunctionArgs } from '@remix/node';
import { themeSessionResolver } from './sessions.server';
export async function loader({ request }: LoaderFunctionArgs) {
  const { getTheme } = await themeSessionResolver(request);
  return {
    theme: getTheme()
  };
}- 📦 Wrap the entire app using ThemeProvider:
import { ThemeProvider } from 'remix-themes';
import { useLoaderData } from '@remix-run/react';
export default function AppWithProviders() {
  const data = useLoaderData<typeof loader>();
  return (
    <ThemeProvider specifiedTheme={data.theme} themeAction="/action/set-theme">
      <App />
    </ThemeProvider>
  );
}
function App() {
    return (
        <html lang="en">
            <!-- ... -->
        </html>
    );
}- ✨ In your <App />component, use theuseThemehook:
import { useLoaderData } from '@remix-run/react';
import { PreventFlashOnWrongTheme, useTheme } from 'remix-themes';
import clsx from 'clsx';
function App() {
  const data = useLoaderData<typeof loader>();
  const [theme] = useTheme();
  return (
    <html lang="en" className={clsx(theme)}>
      <head>
        <meta charSet="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <Meta />
        <PreventFlashOnWrongTheme ssrTheme={Boolean(data.theme)} />
        <Links />
      </head>
      <body className="bg-white text-black dark:bg-black dark:text-white">
        <Outlet />
        <Scripts />
      </body>
    </html>
  );
}- Create a simple theme-toggle.tsxcomponent:
import { Moon, Sun } from 'lucide-react'; // 🥹 Icons from lucide.dev
import { Theme, useTheme } from 'remix-themes';
export function ModeToggle() {
  const [theme, setTheme] = useTheme();
  return (
    <>
      <button onClick={() => setTheme(Theme.LIGHT)}>
        <Sun size={22} strokeWidth={1.4} />
        <span>Light Theme</span>
      </button>
      <button onClick={() => setTheme(Theme.DARK)}>
        <Moon size={22} strokeWidth={1.4} />
        <span>Dark Theme</span>
      </button>
    </>
  );
}Utility to check environment variables (updated)
A utility to check if the environment variables are set in the .env file. It’s like a t3/env library:
- Install Zod:
pnpm i zod -E- Create a env.server.tsfile:
import * as z from 'zod';
const environmentSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  MY_ENV_VARIABLE: z.string().min(1)
});
const environment = () => environmentSchema.parse(process.env);
export { environment };- Usage example with loader:
export function loader() {
  return json({
    publicKeys: {
      MY_ENV_VARIABLE: environment().MY_ENV_VARIABLE
    }
  });
}💡 Other method without Zod:
function getRequiredEnvVarFromObj(
  obj: Record<string, string | undefined>,
  key: string,
  devValue: string = `${key}-dev-value`
) {
  let value = devValue;
  const envVal = obj[key];
  if (envVal) {
    value = envVal;
  } else if (obj.NODE_ENV === 'production') {
    throw new Error(`${key} is a required env variable`);
  }
  return value;
}
export function getRequiredServerEnvVar(key: string, devValue?: string) {
  return getRequiredEnvVarFromObj(process.env, key, devValue);
}