Exploring the process of implementing Tailwind CSS with esbuild

My journey to understand bundlers and plugins has led me to create a workflow using esbuild, tailwindcss, react, typescript, and more. However, I'm struggling to connect tailwind css to eslint and other tools. According to the tailwind css docs, I need postcss for it to work:

npm install -D tailwindcss
npx tailwindcss init

Since there is no mention of postcss in the setup process, I assumed esbuild should handle it via a plugin. There are two options available:

https://github.com/karolis-sh/esbuild-postcss npm i postcss esbuild-postcss -D

and

https://github.com/martonlederer/esbuild-plugin-postcss2 npm i -D esbuild-plugin-postcss2

The first option includes postcss in its installation process, while the second one does not. Despite this, the newer second option seems to build upon the first. Unfortunately, neither of them is functioning as expected. Here is my current esbuild configuration:


    // Esbuild config here...

Additionally, here is a snippet from my package.json file:


    // Package json contents here...

I am utilizing esbuild serve as my development server. When running the css command, the output seems more like a css reset than full tailwind styling. Furthermore, the build command produces copied directives that invoke warnings in VScode. Can anyone provide guidance on how to navigate this bundling process with plugins effectively? Any assistance would be greatly appreciated. Thank you.

Answer №1

If you're looking to seamlessly integrate Tailwind into your workflow, the recommended approach is to utilize their CLI as outlined in their documentation. However, I also explore an alternative method of integrating PostCSS with esbuild to run Tailwind.

Method 1: Leveraging the Tailwind CLI

This method stands out for its simplicity and does not necessitate PostCSS or any esbuild configurations. It is advisable if you don't specifically require other PostCSS functionalities.

To get started, follow the installation instructions provided in the documentation.

npm install -D tailwindcss

npx tailwindcss init

Remember to set up your content keys in your tailwind.config.js.

To build using their CLI, execute the following command (adjusting the path as needed for your project).

npx tailwindcss -i ./src/app.css -o ./public/app.css --minify

You can even combine your esbuild and Tailwind processes by utilizing npm-run-all.

npm I -D npm-run-all
{
"scripts": {
        "build:esbuild": "node ./esbuild.config.js",
        "build:css": "npx tailwindcss -i ./src/app.css -o ./public/app.css --minify",
        "build": "npm-run-all --parallel build:*"
      },
}

A similar setup can be applied for monitoring your development server.

{
"scripts": {
        "build:esbuild": "node ./esbuild.config.js",
        "build:css": "npx tailwindcss -i ./src/app.css -o ./public/app.css --minify",
        "build": "npm-run-all --parallel build:*",
        "watch:esbuild": "node ./esbuild.serve.js",
        "watch:css": "npx tailwindcss -i ./src/app.css -o ./public/app.css --watch",
        "dev": "npm-run-all --parallel watch:*"
      },
}

Method 2: Implementing PostCSS

Conversely, if you are crafting custom CSS and need PostCSS capabilities, or opt to employ esbuild for CSS generation, running Tailwind as a PostCSS plugin alongside esbuild is feasible.

While there isn't a widely maintained standard esbuild plugin for PostCSS, trial different plugins until you find one that suits your needs. One option worth considering is esbuild-style-plugin.

To incorporate this plugin with Tailwind, install Tailwind (refer to their PostCSS installation guidelines).

npm install -D tailwindcss postcss autoprefixer

npx tailwindcss init

Set up your tailwind.config.js, but you won't need a postcss.config.js as that will be configured with esbuild-style-plugin.

Install it:

npm i -D esbuild-style-plugin

Your esbuild configuration will resemble the following (add additional options according to your requirements):

const postCssPlugin = require('esbuild-style-plugin')

require('esbuild')
  .build({
    entryPoints: ['src/app.jsx', 'src/style.css'],
    outdir: 'public',
    bundle: true,
    minify: true,
    plugins: [
      postCssPlugin({
        postcss: {
          plugins: [require('tailwindcss'), require('autoprefixer')],
        },
      }),
    ],
  })
  .catch(() => {
    console.error(`Build error: ${error}`)
    process.exit(1)
  })

Note how we configure our PostCSS plugins here. The plugin also offers additional options for CSS modules or other preprocessors like SASS or LESS.

One aspect to keep in mind is that this plugin may produce a .js file with the same name as your CSS file. To avoid conflicts, consider renaming either the CSS or JS file, or import the CSS file in your JS file (e.g., import './app.css' at the beginning of app.tsx). If opting for imports, remember to exclude app.css from your esbuild config's entrypoints.

I achieved functionality with a basic example (GitHub repository) when originally drafting this answer. Your scenario may have unique challenges, so if viable, I recommend the former method as it reduces dependencies on your bundler.

Answer №2

My VScode is giving me warnings, and I'm not sure how to address them.

There is now a tailwindcss extension available for VS Code.

I am in the process of establishing a more "professional" workflow using esbuild and tailwindcss with react, typescript, and other useful tools.

esbuild has the capability to compile CSS files by default. However, if you are working with TailwindCSS, which involves additional steps to modify your CSS, it might be beneficial to separate the CSS compilation process from your esbuild workflow.

One approach is to update your build script to include the logic for compiling CSS files (using PostCSS and TailwindCSS. Additionally, I am utilizing lightningcss for minification):

import { Buffer } from 'node:buffer';
import { open, writeFile } from 'node:fs/promises';
import { join } from 'node:path';

import * as esbuild from 'esbuild';

import postcss from 'postcss';
import tailwindcss from 'tailwindcss';
import { transform as lightningTransform } from 'lightningcss';


// Build CSS
const INPUT_CSS_FILE = join('src', 'input.css');
const OUTPUT_CSS_FILE = join('build', 'output.css');

const tailwindConfig = { // or import from tailwind.config.js
  content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
  theme: {
    extend: {},
  },
  plugins: [],
};

const postcssInstance = postcss()
  .use(tailwindcss(tailwindConfig));

const f = await open(INPUT_CSS_FILE, 'r');

f.readFile()
  .then((buf) => postcssInstance.process(buf, { from: INPUT_CSS_FILE, to: OUTPUT_CSS_FILE }))
  .then((rs) => rs.css)
  .then((css) => lightningTransform({
    code: Buffer.from(css),
    minify: true,
  }))
  .then(({ code }) => writeFile(OUTPUT_CSS_FILE, code))
  .finally(() => f.close());


// Build JS
const JS_ENTRY_POINT = join('src', 'index.jsx');
const OUTPUT_JS_FILE = join('build', 'js', 'out.js');

await esbuild.build({
  format: 'esm',
  entryPoints: [JS_ENTRY_POINT],
  loader: { '.css': 'empty' }, // turn off default css building by esbuild
  bundle: true,
  outfile: OUTPUT_JS_FILE,
  minify: true,
  define: { "process.env.NODE_ENV": "\"production\"" },
  plugins: [],
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Using a Typescript variable prior to its assignment

I encountered an issue in my Typescript / ReactJS project displaying the error message Variable 'myVar' is used before being assigned. TS2454 This problem arises with my TS version 4.2.3, appearing both in my IDE and during code execution. Inte ...

Keyboard-enabled jQuery function to smoothly scroll the page to a specified heading

I have encountered an issue with a piece of code that generates a list of all the h2 elements on a page with clickable links, but unfortunately, it lacks keyboard accessibility. What I am aiming for is to have the ability to select the this element mentio ...

Utilizing optional parameters with React Router

Imagine I have a page at http://www.example.com/page/#/search set up with the following routing: <Router history={hashHistory}> <Route path='/search/' component={SearchPage} /> </Router> When a user performs a search using t ...

Learn the steps to Toggle Javascript on window resize

Is there a way to toggle Javascript on and off when the window is resized? Currently, resizing the window causes the navigation bar to stick and remain visible above the content. <script> if ( $(window).width() <= 1200 ) { }else{ $('nav& ...

There are two distinct varieties of object arrays

I recently encountered an issue while trying to sort my array of Star Wars episodes by episode ID. The problem emerged when comparing two arrays: one manually inputted in the code snippet labeled as "1" on the screenshot, and the other generated dynamicall ...

When working with ReactJS and NextJS applications, the variables declared in the .env file may sometimes be received as undefined

In my .env.local file, the following variable is defined: REACT_APP_API_PATH=http://localhost:3600/ The .env.local file can be found at the root level of the project directory. Here is how I am attempting to utilize this variable: console.log('node ...

Is there a way for app.use to identify and match requests that begin with the same path?

Given that app.use() responds to any path that starts with /, why does the request localhost:3000/foo match the second method instead of the first? app.use("/",express.static('public'), function(req,res,next) { console.log(& ...

Preventing Duplicate Header Sending in Node.js with Express

Encountering an error "Can't set headers after they are sent to the client" led me to discover that my code is attempting to send two responses that try to modify the previously set headers. To resolve this issue, I added a return keyword to halt fun ...

Employing a combination of IF clauses for form validation

While implementing form validation, I encountered an issue where only the length check was being performed and not the empty string check. This led to only one error message being displayed instead of two separate messages for each scenario. How can I modi ...

Nightwatch.js - Techniques for waiting until an AJAX request is finished

Currently, I am relying on nightwatchJS for browser automation. A common issue I encounter is that much of the content on my webpage gets updated via ajax calls. To ensure accurate testing, I need a way to pause my testing until the ajax call returns resul ...

What is preventing the bundling of my CSS into the application?

I'm facing an issue while setting up a new project using vue.js, scss, and webpack (with express.js on the server side and TypeScript). I copied over the configurations from a previous project where everything was working fine. According to my underst ...

inactive toggle being released

I am facing an issue with my two slider menus, where only the right menu holds the active value when clicked. The left menu slides out only when the toggle button is held down, but I need it to slide out when the toggle is simply clicked and not held down. ...

Steps for creating a tileView from scratch

Have you noticed websites using a tile view layout lately? It seems like a popular trend. The concept involves having multiple elements on an HTML page with various widths and heights. These websites often use JavaScript to randomly arrange each element n ...

The correct functioning of CSS hyperlinks is currently experiencing issues

The links in the "request" division are not displaying properly. The style.css file contains the following code for the links in this division: .header .request a span{border-bottom:1px dotted}.header .request a:hover span{border-bottom:0} Example: berdy ...

Tips for organizing a div into a dock panel

I am seeking guidance on how to align and resize the following divs based on my specific requirements outlined below. Area A Area A should remain at the top with a set height that will not change. Area B The height of Area B should vary between 0 and a m ...

Enhancing the elements in a current array of arrays

I am in the process of creating an array structured like this var qwe = [[a,b],[c],[d]], where a and b serve as identifiers. The values for a - d are being extracted from the DOM. Currently, my JavaScript code is functioning as intended, but I aim to merg ...

Restrict input to only text characters in a textbox using AngularJS

Looking for a way to ensure that users can only input characters into a textbox in my project. Any suggestions on how I can accomplish this? ...

Improving code structure for a unique date feature

I've successfully built a date component (see GIF below). The code is functioning as intended, but I feel it's a bit convoluted and may be difficult for others to grasp. Note: Please refer to the GIF below. Ignore the styling. This is how I&ap ...

The Json parsing failed to deserialize the API response

Struggling to send a JSON to my API, I've tried various solutions but still can't figure out what's going wrong. I'm stuck on this post call function: @PostMapping(path = "ts/sts") public void saveTestStep(@RequestBody Tes ...

What is the most effective way to remove or modify an element in an array when a button is clicked?

I've hit a roadblock because I'm uncertain about how to access and remove elements stored within an array, especially if the user wants to delete from the middle. In this scenario, using pop won't suffice as it removes from the end without c ...