Skip to main content

Migrate from v4 to v5

The biggest change in v5 is not the API name, but the change in the boundaries of responsibilities.

In v4, many projects will first let Tailwind official plug-in generate CSS, and then use weapp-tailwindcss to perform small program escaping. v5 changed to WeappTailwindcss taking over the Tailwind CSS generation link: the same Tailwind input generates selectors and styles available for the mini program on the mini program side; generates Tailwind CSS usable by the browser on the H5/Web side.

So don’t rush to change class names everywhere when migrating. First, straighten out the building links.

Let’s look at the conclusion first

Most projects will do the following:

  1. Upgrade weapp-tailwindcss to v5.
  2. Delete postinstall: "weapp-tw patch".
  3. Remove the Tailwind official generation plug-in from the mini program build.
  4. Register WeappTailwindcss.
  5. Check the entry and scan scope of Tailwind CSS 4.
  6. Don’t simply disable WeappTailwindcss for H5/Web builds; the Taro project must cover both H5 and mini program builds.
  7. Run the mini program construction once, and then test a line to add any value class.

Current v5 documents are migrated to Tailwind CSS 4 by default. The current documentation only maintains Tailwind CSS 4 access instructions.

1. Upgrade dependencies

npm install -D weapp-tailwindcss@5 tailwindcss@4

If these packages only serve small program construction, they can be deleted:

npm uninstall tailwindcss-patch @tailwindcss/vite @tailwindcss/postcss

If there is a separate web application in the same repository, the web application can continue to use @tailwindcss/vite or @tailwindcss/postcss. Limit only to the same applet build: do not let the official Tailwind plug-in and WeappTailwindcss generate Tailwind CSS at the same time.

2. Delete the post-installation patch

Delete the old script in package.json:

package.json
{
"scripts": {
// delete
"postinstall": "weapp-tw patch"
}
}

v5's build link will handle the Tailwind runtime at build time, no post-installation patches are required. Keeping this script usually doesn't bring any benefit, but adds another distraction when troubleshooting.

3. Clean up Tailwind official generated plug-in

Do not register these plug-ins in the mini program build:

postcss.config.js
module.exports = {
plugins: {
tailwindcss: {},
'@tailwindcss/postcss': {},
},
}
vite.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineConfig({
plugins: [
tailwindcss(),
],
})

Business PostCSS plug-ins can be retained, such as autoprefixer, the framework's own PostCSS plug-in, compression plug-in, etc. What needs to be deleted is the Tailwind CSS generation entry.

4. Check Tailwind CSS entry

Tailwind CSS 4 uses CSS-first entry:

src/app.css
@import "tailwindcss";
@source "./**/*.{html,js,ts,jsx,tsx,vue,wxml,axml,ttml,mpx,uts,uvue}";
@source not "./uni_modules";
@source not "../node_modules";
@source not "../dist";
@source not "../unpackage";

Please place the entry of Tailwind CSS 4 in a pure .css file. Do not write @import "tailwindcss" directly into the scss, less, or sass files. Business preprocessing styles can be imported into this CSS file, but the Tailwind entry itself is best kept simple.

5. Register WeappTailwindcss

Vite project:

vite.config.ts
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'

const projectRoot = dirname(fileURLToPath(import.meta.url))

export default defineConfig({
plugins: [
WeappTailwindcss({
cssEntries: [
resolve(projectRoot, 'src/app.css'),
],
cssOptions: {
rem2rpx: true,
},
}),
],
})

In the uni-app Vite project, place it after uni():

vite.config.ts
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'

const projectRoot = dirname(fileURLToPath(import.meta.url))

export default defineConfig({
plugins: [
uni(),
WeappTailwindcss({
cssEntries: [
resolve(projectRoot, 'src/app.css'),
],
cssOptions: {
rem2rpx: true,
},
}),
],
})

Webpack project:

webpack.config.js
const path = require('node:path')
const { WeappTailwindcss } = require('weapp-tailwindcss/webpack')

module.exports = {
plugins: [
new WeappTailwindcss({
cssEntries: [
path.resolve(__dirname, 'src/app.css'),
],
cssOptions: {
rem2rpx: true,
},
}),
],
}

Projects such as Taro, Mpx, and uni-app Webpack have different mounting locations. Just copy the complete writing method according to the corresponding frame page. There is one point to grasp when migrating: hand over Tailwind generation to WeappTailwindcss, and do not generate a second copy of Tailwind CSS from PostCSS or the official Vite plug-in.

Taro migration needs to cover H5 and mini programs

When migrating Taro, do not just register the plug-in in the mini program link. WeappTailwindcss of v5 will automatically switch to the web target based on TARO_ENV=h5, so the H5 build should also retain the plug-in.

In the Webpack project, mini.webpackChain and h5.webpackChain are both registered once:

config/index.[jt]s
const path = require('node:path')
const { WeappTailwindcss } = require('weapp-tailwindcss/webpack')

const weappTailwindcssOptions = {
cssOptions: {
rem2rpx: true,
},
cssEntries: [
path.resolve(__dirname, '../src/app.css'),
],
}

function registerWeappTailwindcss(chain) {
chain.merge({
plugin: {
install: {
plugin: WeappTailwindcss,
args: [weappTailwindcssOptions],
},
},
})
}

module.exports = {
mini: {
webpackChain(chain) {
registerWeappTailwindcss(chain)
},
},
h5: {
webpackChain(chain) {
registerWeappTailwindcss(chain)
},
},
}

In the Vite project, place the plug-in in config/index of compiler.vitePlugins. Don't just write a separate vite.config.ts, because it is usually only loaded when the applet is running, and H5 will not take this configuration.

config/index.ts
import path from 'node:path'
import type { Plugin } from 'vite'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'

export default {
compiler: {
type: 'vite',
vitePlugins: [
WeappTailwindcss({
cssOptions: {
rem2rpx: true,
},
cssEntries: [
path.resolve(__dirname, '../src/app.css'),
],
}),
] as Plugin[],
},
}

6. Write clear cssEntries

Tailwind CSS 4 projects should always write cssEntries and use the absolute path resolved from the project root. Explicit entry is more stable and easier to troubleshoot cross-framework, cross-platform, subcontracting and multi-entry issues.

These cases must be written explicitly:

  • There are multiple CSS-first entries in the Tailwind CSS 4 project.
  • Ordinary sub-packaging and independent sub-packaging have their own CSS-first entry.
  • Entry CSS is not introduced directly by the framework, or indirectly by a custom plug-in/loader.
  • Webpack, Gulp, custom builds.
  • The build log indicates that the Tailwind CSS entry is not found.
  • uni-app x + Tailwind CSS 4, especially the HBuilderX project.

Even for single-entry Vite projects, explicit writing is recommended. It is not intended to replace import, but to allow WeappTailwindcss to stably read the @source, @config and Tailwind instructions in the entry CSS.

vite.config.ts
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'

const projectRoot = dirname(fileURLToPath(import.meta.url))

WeappTailwindcss({
cssOptions: {
rem2rpx: true,
},
cssEntries: [
resolve(projectRoot, 'src/app.css'),
],
})

Multiple entry or subcontracting projects:

vite.config.ts
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'

const projectRoot = dirname(fileURLToPath(import.meta.url))

WeappTailwindcss({
cssEntries: [
resolve(projectRoot, 'src/app.css'),
resolve(projectRoot, 'src/sub-normal/pages/index.css'),
resolve(projectRoot, 'src/sub-independent/pages/index.css'),
],
})

cssEntries Writes the absolute path to the CSS entry containing the @import "tailwindcss" or @tailwind directive. Just write in all the multiple entries.

7. Don’t disable H5/Web builds across the board

This paragraph often appears in v4 projects:

vite.config.ts
const isH5 = process.env.UNI_PLATFORM === 'h5'

WeappTailwindcss({
disabled: isH5,
})

After migrating to v5, first delete this H5/Web disabling logic:

vite.config.ts
WeappTailwindcss({
cssOptions: {
rem2rpx: true,
},
})

v5 automatically switches targets based on common environment variables. For example, UNI_PLATFORM=h5/app/app-plus, The uni-app x native App target such as UNI_UTS_PLATFORM=h5/web/web-* will not be regarded as Web, and there is no need to add TARO_ENV=h5.

If you need to specify it explicitly in a custom build, you can also write:

vite.config.ts
WeappTailwindcss({
generator: {
target: 'web',
},
})

disabled is still useful, but it's suitable for "skipping the plugin entirely" builds, such as some RN, Harmony, or standalone native builds. For H5/Web targets of uni-app, uni-app x, Taro, Mpx, Weapp-vite, it is usually not necessary to disable it.

8. uni-app x and HBuilderX

uni-app x recommends using the uniAppX preset:

vite.config.ts
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { uniAppX } from 'weapp-tailwindcss/presets'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'

const projectRoot = dirname(fileURLToPath(import.meta.url))

export default defineConfig({
plugins: [
uni(),
WeappTailwindcss(
uniAppX({
base: projectRoot,
cssEntries: [
resolve(projectRoot, 'main.css'),
],
cssOptions: {
rem2rpx: true,
},
}),
),
],
})

uni-app x projects should explicitly write cssEntries, pointing to the pure CSS entry.

HBuilderX can be verified separately by target when running locally:

npm run dev:mp-weixin
npm run dev:android:emulator
npm run dev:ios:simulator

If you use the local e2e in the warehouse, it is:

npm run e2e:hbuilderx:local:app
npm run e2e:hbuilderx:local:android
npm run e2e:hbuilderx:local:ios

The iOS simulator requires full Xcode. xcode-select -p should point to /Applications/Xcode.app/Contents/Developer and xcodebuild -checkFirstLaunchStatus should return 0.

9. Verify whether the migration is really successful

It’s not enough to see a build succeed. Test at least one new class after migration.

Temporarily write on the page:

<view class="mt-[11px] w-[173px] rounded-[13px] bg-[#102938] p-4 text-[#f7fbff]">
v5 check
</view>

Then run the corresponding target:

npm run dev:mp-weixin

Run HMR again for the H5/Web project: after starting the dev server, change any value class several times to confirm that the CSS will be refreshed. For example, change bg-[#102938] to bg-[#0f5132], bg-[#7c2d12], and bg-[#4338ca] in sequence. If there is a style in the first pass, then any added value class will not have a style, usually because the entry scan or HMR dependency is not connected.

Don’t just look at manifest.json on the app. The Android product of uni-app For example:

bg-[#102938] -> bg-_b_h102938_B
text-[#f7fbff] -> text-_b_hf7fbff_B
w-[173px] -> w-_b173px_B

FAQ

PhenomenonPriority Check
No style at allWhether the CSS entry is introduced by the builder, or whether it needs to be configured cssEntries
Tailwind CSS 4 class name is not generatedWhether @source in the CSS entry covers the source code and whether dist / unpackage is excluded
The styles are repeated or the order is strangeIs tailwindcss / @tailwindcss/postcss / @tailwindcss/vite also registered in the mini program build
Still patching after installationIs package.json still retained?
The class name in the JS string is not translatedWhether this class has been scanned by Tailwind first; v5 cannot guess ordinary strings
H5 style becomes applet escape classCheck the Web target environment variable and explicitly set generator.target: 'web' if necessary
uni-app x App style is missingCheck whether the uniAppX preset is enabled and whether Tailwind CSS 4 has cssEntries configured

Things that can be deleted after migration

  • tailwindcss-patch
  • postinstall: "weapp-tw patch"
  • @tailwindcss/vite in mini program construction
  • @tailwindcss/postcss in mini program construction
  • tailwindcss PostCSS plug-in in mini program construction
  • disabled: isH5 logic written only for H5/Web

Don't rush to delete business PostCSS plug-ins, framework plug-ins, and Tailwind configuration files. Whether they are retained or not depends on the project itself.

Continue reading