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:
- Upgrade
weapp-tailwindcssto v5. - Delete
postinstall: "weapp-tw patch". - Remove the Tailwind official generation plug-in from the mini program build.
- Register
WeappTailwindcss. - Check the entry and scan scope of Tailwind CSS 4.
- Don’t simply disable
WeappTailwindcssfor H5/Web builds; the Taro project must cover both H5 and mini program builds. - 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
- Yarn
- pnpm
- Bun
npm install -D weapp-tailwindcss@5 tailwindcss@4
yarn add --dev weapp-tailwindcss@5 tailwindcss@4
pnpm add -D weapp-tailwindcss@5 tailwindcss@4
bun add --dev weapp-tailwindcss@5 tailwindcss@4
If these packages only serve small program construction, they can be deleted:
- npm
- Yarn
- pnpm
- Bun
npm uninstall tailwindcss-patch @tailwindcss/vite @tailwindcss/postcss
yarn remove tailwindcss-patch @tailwindcss/vite @tailwindcss/postcss
pnpm remove tailwindcss-patch @tailwindcss/vite @tailwindcss/postcss
bun remove 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:
{
"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:
module.exports = {
plugins: {
tailwindcss: {},
'@tailwindcss/postcss': {},
},
}
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:
@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:
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():
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:
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:
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.
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.
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:
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:
const isH5 = process.env.UNI_PLATFORM === 'h5'
WeappTailwindcss({
disabled: isH5,
})
After migrating to v5, first delete this H5/Web disabling logic:
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:
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:
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
- Yarn
- pnpm
- Bun
npm run dev:mp-weixin
npm run dev:android:emulator
npm run dev:ios:simulator
yarn dev:mp-weixin
yarn dev:android:emulator
yarn dev:ios:simulator
pnpm run dev:mp-weixin
pnpm run dev:android:emulator
pnpm run dev:ios:simulator
bun run dev:mp-weixin
bun run dev:android:emulator
bun run dev:ios:simulator
If you use the local e2e in the warehouse, it is:
- npm
- Yarn
- pnpm
- Bun
npm run e2e:hbuilderx:local:app
npm run e2e:hbuilderx:local:android
npm run e2e:hbuilderx:local:ios
yarn e2e:hbuilderx:local:app
yarn e2e:hbuilderx:local:android
yarn e2e:hbuilderx:local:ios
pnpm run e2e:hbuilderx:local:app
pnpm run e2e:hbuilderx:local:android
pnpm run e2e:hbuilderx:local:ios
bun run e2e:hbuilderx:local:app
bun run e2e:hbuilderx:local:android
bun 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
- Yarn
- pnpm
- Bun
npm run dev:mp-weixin
yarn dev:mp-weixin
pnpm run dev:mp-weixin
bun 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
| Phenomenon | Priority Check |
|---|---|
| No style at all | Whether the CSS entry is introduced by the builder, or whether it needs to be configured cssEntries |
| Tailwind CSS 4 class name is not generated | Whether @source in the CSS entry covers the source code and whether dist / unpackage is excluded |
| The styles are repeated or the order is strange | Is tailwindcss / @tailwindcss/postcss / @tailwindcss/vite also registered in the mini program build |
| Still patching after installation | Is package.json still retained? |
| The class name in the JS string is not translated | Whether this class has been scanned by Tailwind first; v5 cannot guess ordinary strings |
| H5 style becomes applet escape class | Check the Web target environment variable and explicitly set generator.target: 'web' if necessary |
| uni-app x App style is missing | Check whether the uniAppX preset is enabled and whether Tailwind CSS 4 has cssEntries configured |
Things that can be deleted after migration
tailwindcss-patchpostinstall: "weapp-tw patch"@tailwindcss/vitein mini program construction@tailwindcss/postcssin mini program constructiontailwindcssPostCSS plug-in in mini program constructiondisabled: isH5logic 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.