Using React Router (Remix) with Material Tailwind components

Material UI is a great Google React UI component library. It looks great, provides consistent look for web applications, but it has two disadvantages:

  • The icon library has more than 9000 icons, it takes a long time to load all of them, as there is no tree shaking to ignore the unused items.
  • The Tailwind styling classes don’t always work, because the Material UI built-in themes of colors and other settings take precedent.

Material Tailwind is an opensource library recreating the consistent look of Material UI, but allowing Tailwind themes to control the look.

To set a React Router (Remix) web application to use Material Tailwind

Install Tailwind CSS with React Router (Remix)

Install Tailwind CSS

pnpm install tailwindcss @tailwindcss/vite

Configure the Vite plugin. Add the bold lines to the vite.config.js file

import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
  plugins: [
    tailwindcss(),
    reactRouter(),
    tsconfigPaths(),
  ],
});

Add to the app.css file

@import "tailwindcss";

See Install Tailwind CSS with React Router

Install @material-tailwind/react as a dependency

Execute the command

pnpm i @material-tailwind/react

Create the tailwind.config.js file in the root of the web application directory

const withMT = require("@material-tailwind/react/utils/withMT");
module.exports = withMT({
  content: ["./app/**/*.{js,jsx,ts,tsx}"], // Add your template paths here
  theme: {
    extend: {},
  },
  plugins: [],
});

If you are using a monorepo, to host multiple applications in the same repository, and the node_modules directory is not in the application directory, use this tailwind.config.js file in the root of the web application

import type { Config } from "tailwindcss";
 
import withMT from "@material-tailwind/react/utils/withMT";
 
export default withMT({
  content: [
    "./app/**/*.{js,ts,jsx,tsx}",
    "path-to-your-node_modules/@material-tailwind/react/components/**/*.{js,ts,jsx,tsx}",
    "path-to-your-node_modules/@material-tailwind/react/theme/components/**/*.{js,ts,jsx,tsx}",
  ],
  theme: {
    extend: {},
  },
  plugins: [],
} satisfies Config);

Theme Provider

Wrap the entire application with the ThemeProvider. Create the app/entry.client file

import { RemixBrowser } from "@remix-run/react";
import { startTransition } from "react";
import { hydrateRoot } from "react-dom/client";
import { ThemeProvider } from "@material-tailwind/react";
 
startTransition(() => {
  hydrateRoot(
    document,
    <ThemeProvider>
      <RemixBrowser />
    </ThemeProvider>
  );
});

Web page example

This example displays a button using the Material Tailwind library

import MaterialTailwind from "@material-tailwind/react";
const { Button } = MaterialTailwind;
 
export default function Example() {
  return <Button>Button</Button>;
}

See Material Tailwind with Remix

React web application first page load is very slow

The React web application’s first page can load very slowly in development mode when SSR (Server Side Rendering) is enabled. There can be many causes, but there is one that can be eliminated easily. If the application uses Google’s Material UI library the loading of the more than 9000 icons can take a long time.

The main issue

The main issue was @mui/icons-material being imported in the NavigationMenu.tsx and Drawer.tsx components. This package contains thousands of icons (~9000 modules) and loads synchronously, causing Vite to do expensive on-demand optimization on first page load.

Changes Made

  1. NavigationMenu.tsx – Replaced @mui/icons-material imports with lucide-react:
    • ChevronRight → ChevronRight from lucide-react
    • ExpandMore → ChevronDown from lucide-react
    • Menu → Menu from lucide-react
  2. Drawer.tsx – Replaced Close icon from @mui/icons-material with X (aliased as Close) from lucide-react
  3. vite.config.ts – Optimized the configuration:
    • Removed workspace packages from optimizeDeps.include (they link directly to source)
    • Added more MUI submodules to pre-bundling (Collapse, Divider, Fade, Grow, List components, etc.)
    • Removed @mui/icons-material from SSR noExternal since it’s no longer used

Performance Impact

  • Before: Loading @mui/icons-material would trigger parsing/transforming thousands of modules
  • Afterlucide-react is much smaller (~400 icons, tree-shakeable) and already pre-bundled

The first page load in dev mode should now be significantly faster.

Disable the Caps Lock key in Windows 11

To disable the Caps Lock key in Windows 11 you can edit the registry.

DISCLAIMER: THIS CAN ALTER HOW YOUR KEYBOARD WORKS, DO IT AT YOUR OWN RISK!!!!

For the original full instructions see

Short of physically removing the caps-lock key from the keyboard, is there a way to disable CAPS LOCK?

The details are

  • Open the registry editor, regedit
  • BACK UP THE ENTIRE REGISTRY BEFORE YOU MAKE ANY CHANGES
    • Right-click the Computer element and select Export
    • Save the file in a location where you will find it
  • Add the Scancode Map binary value to the  HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout key with the value

00 00 00 00 00 00 00 00 02 00 00 00 00 00 3A 00 00 00 00 00

  • Check, and double check, and triple check the value before saving it. A wrong value can disable certain keys on your keyboard until you are able to reverse the change. If you disable the wrong key that is necessary to log into Windows, use the on-screen keyboard with the mouse to log in and use the mouse to open Regedit and correct the error.
  • Restart the computer for the changes to take effect

Using GitHub Copilot Coding Agent

Copilot Coding Agent can work entirely online without you knowing anything about software development. To use it

  • Open the repository in GitHub
  • Create an Issue
  • Write the instructions in the descriptions field
  • Assign it to Copilot

The agent will create a new branch and based on your description it will make code modifications.

  • To see the progress click the View session button

Once the agent completed the task, it will request a review

  • On the top of the screen you will see the message:
    Copilot requested your review on this pull request.
    Click the Add your review button
  • To enter line specific comments or instructions, hover above the plus sign and select the blue plus sign to open the Write panel.
  • You can type any message into the review field to share your thoughts with other humans, Copilot will ignore those. To give additional instructions to Copilot, start the message with @copilot
  • To start the review, click the Start a review button
  • To finish the review click the Finish your review button
  • You can write additional instructions if necessary and click the Submit review button
  • While the agent is working, you can give additional instructions in the bottom chat window
  • To go back from the session to the pull request, click the View pull request button at the top of the page
  • At the top of the screen click the Add your review button
  • Click the Review changes button
  • If you are satisfied with the result scroll down to the bottom of the page and click the Merge pull request button
  • Click the Confirm merge button
  • Click the Delete branch button

Preparing the GitHub Copilot Coding Agent environment

To make sure the GitHub Copilot Coding Agent understands your application you give instructions with

  • Project overview, including it’s purpose, goals, and any relevant background information.
  • Program architecture, standards and conventions that should be followed,
  • Useful commands or scripts for common tasks

To save the instructions

Create a new branch

  • On the code tab click the branches icon
  • In the upper right corner click the New branch button
  • Name the branch
    prepare-environment
  • Go back to the Code tab and click the down arrow on the current branch field and select the new branch
  • Navigate to the .github directory
  • If the copilot-instructions.md file does not exist in the upper right corner click the Add file button
  • Click the copilot-instructions.md file to open it
  • In the upper right corner of the edit window click the Edit button
  • You can add links to refer to other documents
  • And other instructions
  • When finished click the Commit changes button in the upper right corner
  • Click the Commit changes button on the new panel
  • The copilot-instructions-ext.md file contains more recommendations with much more details.

Prepare the GitHub Copilot Coding Agent environment

Specify the required tools in the copilot-setup-steps.yml file

  • Stay in the prepare-environment branch
  • Navigate to the .github/workflows/ directory
  • In the upper right corner click the Add file button
  • Select Create new file
  • The following example pre-installs Python and MongoDB in the Copilot environment
name: "Copilot Setup Steps"

on: workflow_dispatch
jobs:
  # This is the required job name. If different, Copilot will ignore it.
  copilot-setup-steps:
    runs-on: ubuntu-latest
 
  # Starts a MongoDB service for Copilot to use during its session.
    services:
     mongo:
       image: mongo:7
       ports:
         - 27017:27017

    # Grant Copilot early access to read the repository content.
    permissions:
      contents: read

    steps:
      - name: Checkout code
        uses: actions/checkout@v5

      - name: Set up Python
        uses: actions/setup-python@v6
        with:
          python-version: "3.13"
          cache: "pip"

      - name: Install Python dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -r src/requirements.txt
  • Enter the file name copilot-setup-steps.yml and click the Commit changes button
  • On the next window click the Commit changes button again
  • On the Pull requests tab click the Compare & pull request button
  • Click the Create pull request button
  • Wait, and let the agent check the changes before you stat the merge
  • Click the Merge pull request button
  • Click the Confirm merge button
  • Click the Delete branch button

Manage multiple tasks with the Agents Panel

  • To open the Agents Panel, in the upper right corner of the page click the Open agents panel button
  • Make sure the current repository is selected
  • When you press enter the task is submitted. The window stays open to monitor the progress and to enter for more instructions.
  • Click the task to see the session logs

To open the Agents panel in full screen mode navigate to https://github.com/copilot/agents

If you assign multiple parallel tasks to the agent, make sure the code changes don’t overlap, otherwise merge conflicts will appear.

Working with Bitbucket

Install Atlassian SourceTree

Install Atlassian SourceTree, the free Bitbucket UI from https://www.sourcetreeapp.com/

Configure Visual Studio Code Git extension

The Git extension is a part of Visual Studio Code. If there is no Git executable installed on the system, set the portable Git executable installed by SourceTree.

Open the C:\Users\YOUR_USER_NAME\AppData\Roaming\Code\User\settings.json and set

"git.path": "%USERPROFILE%\AppData\Local\Atlassian\SourceTree\git_local\bin\git.exe"
Add Git to the Path

Add the directory path of the git.exe file to the System path environment variable:
%USERPROFILE%\AppData\Local\Atlassian\SourceTree\git_local\bin

Restart any open PowerShell or Terminal for the change to take effect.

Working with mono repos

Once the mono repo is configured, we can install dependencies and build applications from the top level using the filter.

Install all dependencies
pnpm -r install
Remove duplicate dependencies

No -r option is necessary

pnpm dedupe

To remove the duplicates of a specific dependency

pnpm dedupe @emotion/react

To list all references of a dependency

pnpm ls @emotion/react
Typecheck all packages and applications
pnpm -r typecheck
Build all apps and packages
pnpm -r build
Build a specific app from the root
pnpm --filter MY_APP build
Run a specific application from the root
pnpm --filter MY_APP dev

Working with apps and packages that are not part of the workspace apps and packages

If the mono repo contains other apps, like Figma Make downloads that are applications, but not part of the released products, the –filter option does not work. We can still interact with them, but we need to start the commands in their subdirectory and reference them with the dot.

To install the dependencies of an app that is not part of the release
cd MY_DESIGN_APP_FOLDER
pnpm install .
To build the app that is not part of the release
pnpm build .
To run the app that is not part of the release
pnpm dev .

Refresh stale references

If you make configuration changes, old references and caches stay in the file system. To force the refresh, delete the ephemeral directories, so the next dependency install, application build, and run will recreate them.

cd apps/MY_APP
rm -rf node_modules/.vite-temp # just to delete the vite temp directory
rm -rf node_modules            # all node modules and the vite temp dir
rm -rf .vite                   # vite cache
pnpm install                   # reinstall all dependencies

Set the owner of directories and files recursively

If your user account cannot access certain folders or files, you can recursively set the owner.

  • Open a PowerShell terminal with admin rights
  • To set the owner to a specific user account
icacls "C:\THE_TOP_DIRECTORY" /inheritance:e /grant "MY_USER_NAME:(OI)(CI)F" /T
  • To use the owner of the parent directory
icacls "C:\THE_TOP_DIRECTORY" /reset /T

Creating a mono repo for multiple applications

If you are planning to develop multiple applications working together, using the same technology, it is advantageous to place all of them in the same Git repository. This way you can use relative paths to refer to shared functions to access the same database, React components, and images to make the pages look consistent. As all are in the same repository, the CI/CD pipeline will also find those during application building and testing.

If you use Node.js packages, use PNPM for package management to rationalize the node modules and store them in the top level node_modules directory and use symbolic links to point to those from other locations. See Install pnpm.

To set up the mono repo for multiple Node.js applications

Create the directory structure

Create the root directory of the repository and the apps and packages subdirectories.

mkdir -p MY_APP_SUITE
cd MY_APP_SUITE
mkdir -p apps
mkdir -p packages

Add configuration files to the root directory

.gitignore

This is the minimum you should have for any Node.js project to exclude secrets and libraries

.DS_Store
.env
/node_modules/

# React Router
/.react-router/
/build/

package.json

Set the versions of node and pnpm in the file below and save it in the root of the repository

{
  "name": "MY_APP_SUITE_NAME",
  "version": "1.0.0",
  "private": true,
  "description": "MY DESCRIPTION",
  "main": "index.js",
  "scripts": {
  },
  "keywords": [],
  "author": "MY NAME",
  "dependencies": {
  },
  "devDependencies": {
  },
  "config": {
  },
  "engines": {
    "node": ">=24.11.0",
    "pnpm": ">=10.15.1"
  },
  "packageManager": "pnpm@10.15.1"
}

pnpm-workspace.yaml

This file tells PNPM where the applications and shared packages will be to be able to reuse node-packages.
** tells PNPM to search for package.json files recursively.

packages:
  - "apps/**"
  - "packages/**"

Add the applications

If you already have existing applications, copy their directory structures into the apps directory and delete the node_modules directories. PNPM will recreate those and create symbolic links to point to the top level node_modules directory.

To create a new React Router web application, open a terminal in the apps directory and follow the instructions at Creating a new React Router application.

As we use PNPM to rationalize the node modules for all applications, do not install the node modules in the individual application directories. Once the application structure been created open a terminal in the root of the mono repo and execute

pnpm install

Based on the packages list in the pnpm-workspace.yaml file, PNPM will install all necessary node modules for all applications and packages in the entire workspace, which is the current mono repo.

Download a Figma Make to your local computer and run it locally as an application

Figma make can generate web applications with AI assistance. To download and run the generated web application on your local computer

  • Open Figma Make
  • On the top of the page select the Code tab
  • In the upper right corner click the Download code button
  • Extract the downloaded Zip file into a directory
  • Install pnpm on your computer. See Install pnpm
  • Open a terminal in the root of the application directory structure, where the package.json file is located
  • Execute the command in the terminal
    pnpm run dev

Finding unidentified processes in WSL

If an unidentified process is generating a web site

Try to load it in an incognito / Private window
Open http://localhost:PORT/... in a brand new private window.

  • Works there too → real server is running.
  • Fails there → old tab is just showing cached content.

curl test

In a Windows or in WSL:

curl -v http://localhost:8081/
  • Connection refused / timeout → backend is dead.
  • HTTP 200 with HTML → something is definitely serving.

Who owns the port

In a Windows PowerShell window with Admin rights:

netstat -abno | findstr :8081

In a Windows PowerShell window with Admin rights:

tasklist /FI "PID eq <PID from above>"

In WSL (if wslrelay.exe is involved):

sudo ss -lntp | grep 8081
sudo lsof -i :8081

To kill the processes in WSL, use the PIDs returned by the command above

sudo kill PID1 PID2

WARNING: To permanently disable the docker and docker.socket in WSL you may execute the following commands, but maybe those processes are necessary for the normal operation of the WSL system.

# 1. Disable Docker and its socket so they don't start automatically
sudo systemctl disable docker.service docker.socket

# 2. Stop the socket right now so nothing can auto-start dockerd in this session
sudo systemctl stop docker.socket

# 3. Sanity check
systemctl status docker
systemctl status docker.socket