NutUI is a mobile UI component library developed by JD.com. It provides a rich set of components for building high‑quality mobile web apps with Vue 3. It supports both Vue 2 and Vue 3, with extensive documentation and a design system tailored for e‑commerce and enterprise applications.
#Installation
#Using npm
npm install @nutui/nutui
#Using yarn
yarn add @nutui/nutui
#Basic Usage
#Full Import
Import all components globally in your main.js:
import { createApp } from 'vue';
import App from './App.vue';
import NutUI from '@nutui/nutui';
import '@nutui/nutui/dist/style.css';
const app = createApp(App);
app.use(NutUI);
app.mount('#app');
#On-Demand Import (Recommended)
Use unplugin-vue-components to automatically import components as you use them.
- Install the plugin:
npm install unplugin-vue-components -D
- Configure
vite.config.js:
import Components from 'unplugin-vue-components/vite';
import NutUIResolver from '@nutui/nutui/dist/resolvers';
export default {
plugins: [
Components({
resolvers: [NutUIResolver()],
}),
],
};
Now you can use NutUI components directly in your templates without importing them manually.
#Theme Customization
NutUI uses SCSS variables for theming. You can override them by creating a custom SCSS file.
- Create a file, e.g.,
src/styles/nutui-theme.scss:
// Custom primary color
$primary-color: #ff6600;
$primary-color-end: #ff8533;
// Button styles
$button-primary-background-color: $primary-color;
$button-primary-border-color: $primary-color;
- Import this file before NutUI styles in your
main.js:
import './styles/nutui-theme.scss';
import '@nutui/nutui/dist/style.css';
#Common Components
#Button
<template>
<div class="demo">
<nut-button type="primary">Primary Button</nut-button>
<nut-button type="success">Success Button</nut-button>
<nut-button type="danger">Danger Button</nut-button>
<nut-button plain>Plain Button</nut-button>
<nut-button disabled>Disabled</nut-button>
</div>
</template>
<script setup>
// No import needed when using on-demand
</script>
#Cell (List Item)
<template>
<nut-cell-group>
<nut-cell title="Title" desc="Description" is-link></nut-cell>
<nut-cell title="With Icon">
<template #icon>
<nut-icon name="star"></nut-icon>
</template>
</nut-cell>
<nut-cell title="Custom Slot">
<template #link>
<span>Custom content</span>
</template>
</nut-cell>
</nut-cell-group>
</template>
#Popup
<template>
<div>
<nut-button @click="showPopup = true">Open Popup</nut-button>
<nut-popup v-model:visible="showPopup" position="bottom" :style="{ height: '200px' }">
<div class="popup-content">Popup content</div>
</nut-popup>
</div>
</template>
<script setup>
import { ref } from 'vue';
const showPopup = ref(false);
</script>
<style scoped>
.popup-content {
padding: 20px;
text-align: center;
}
</style>
#Toast
<template>
<nut-button @click="showToast">Show Toast</nut-button>
</template>
<script setup>
import { showToast } from '@nutui/nutui';
const showToast = () => {
showToast.text('Operation successful', {
duration: 2000,
position: 'center',
});
};
</script>
#Dialog
<template>
<nut-button @click="showDialog">Open Dialog</nut-button>
</template>
<script setup>
import { showDialog } from '@nutui/nutui';
const showDialog = () => {
showDialog({
title: 'Confirm',
content: 'Are you sure you want to proceed?',
onConfirm: () => {
console.log('Confirmed');
},
onCancel: () => {
console.log('Cancelled');
},
});
};
</script>
#Form Components
#Input
<template>
<nut-form>
<nut-form-item label="Username">
<nut-input v-model="username" placeholder="Enter username" />
</nut-form-item>
<nut-form-item label="Password">
<nut-input v-model="password" type="password" placeholder="Enter password" />
</nut-form-item>
<nut-button type="primary" block @click="handleSubmit">Submit</nut-button>
</nut-form>
</template>
<script setup>
import { ref } from 'vue';
const username = ref('');
const password = ref('');
const handleSubmit = () => {
console.log({ username: username.value, password: password.value });
};
</script>
#Radio
<template>
<nut-radio-group v-model="selected">
<nut-radio label="1">Option 1</nut-radio>
<nut-radio label="2">Option 2</nut-radio>
<nut-radio label="3">Option 3</nut-radio>
</nut-radio-group>
</template>
<script setup>
import { ref } from 'vue';
const selected = ref('1');
</script>
#Checkbox
<template>
<nut-checkbox-group v-model="checkedList">
<nut-checkbox label="1">Option 1</nut-checkbox>
<nut-checkbox label="2">Option 2</nut-checkbox>
<nut-checkbox label="3">Option 3</nut-checkbox>
</nut-checkbox-group>
</template>
<script setup>
import { ref } from 'vue';
const checkedList = ref(['1', '2']);
</script>
#Layout Components
#Grid
<template>
<nut-grid :columns="3" :gap="10">
<nut-grid-item>
<nut-icon name="home" />
<span>Home</span>
</nut-grid-item>
<nut-grid-item>
<nut-icon name="star" />
<span>Star</span>
</nut-grid-item>
<nut-grid-item>
<nut-icon name="cart" />
<span>Cart</span>
</nut-grid-item>
</nut-grid>
</template>
#Tabs
<template>
<nut-tabs v-model="activeTab">
<nut-tab-pane title="Tab 1">Content 1</nut-tab-pane>
<nut-tab-pane title="Tab 2">Content 2</nut-tab-pane>
<nut-tab-pane title="Tab 3">Content 3</nut-tab-pane>
</nut-tabs>
</template>
<script setup>
import { ref } from 'vue';
const activeTab = ref(0);
</script>
#Navigation Components
#Tabbar
<template>
<nut-tabbar v-model="activeTab">
<nut-tabbar-item tab-key="home" icon="home">Home</nut-tabbar-item>
<nut-tabbar-item tab-key="category" icon="category">Category</nut-tabbar-item>
<nut-tabbar-item tab-key="cart" icon="cart">Cart</nut-tabbar-item>
<nut-tabbar-item tab-key="user" icon="user">Profile</nut-tabbar-item>
</nut-tabbar>
</template>
<script setup>
import { ref } from 'vue';
const activeTab = ref('home');
</script>
#Data Display Components
#Swiper
<template>
<nut-swiper :autoplay="3000" :height="200">
<nut-swiper-item v-for="item in list" :key="item">
<img :src="item" style="width: 100%; height: 100%; object-fit: cover" />
</nut-swiper-item>
</nut-swiper>
</template>
<script setup>
const list = [
'https://via.placeholder.com/400x200?text=Slide+1',
'https://via.placeholder.com/400x200?text=Slide+2',
'https://via.placeholder.com/400x200?text=Slide+3',
];
</script>
#Badge
<template>
<nut-badge value="99+" dot>
<nut-icon name="message" size="24px" />
</nut-badge>
</template>
#Feedback Components
#Loading
<template>
<nut-loading type="circular" />
<nut-loading type="spinner" />
<nut-loading type="dots" />
</template>
#Progress
<template>
<nut-progress :percentage="70" color="#ff6600" />
</template>
#Integration with Vue Router
NutUI components work seamlessly with Vue Router. For example, you can use nut-tabbar with router links:
<template>
<nut-tabbar v-model="activeTab" router>
<nut-tabbar-item to="/home" tab-key="home" icon="home">Home</nut-tabbar-item>
<nut-tabbar-item to="/category" tab-key="category" icon="category">Category</nut-tabbar-item>
<nut-tabbar-item to="/cart" tab-key="cart" icon="cart">Cart</nut-tabbar-item>
<nut-tabbar-item to="/user" tab-key="user" icon="user">Profile</nut-tabbar-item>
</nut-tabbar>
</template>
#Internationalization
NutUI supports multiple languages. To change the language, import the desired locale:
import { createApp } from 'vue';
import App from './App.vue';
import NutUI from '@nutui/nutui';
import '@nutui/nutui/dist/style.css';
import enUS from '@nutui/nutui/dist/locale/en-US';
const app = createApp(App);
app.use(NutUI, { locale: enUS });
app.mount('#app');
#Best Practices
- Use on‑demand import with
unplugin-vue-componentsto keep bundle size minimal. - Override SCSS variables early to maintain consistent branding.
- Combine NutUI with Pinia for state management in larger apps.
- Test on both iOS and Android devices to ensure component behavior matches expected.
- Refer to the official NutUI documentation for the latest updates and advanced usage.
#Troubleshooting
#Common Issues
- Styles not loading: Ensure you’ve imported
@nutui/nutui/dist/style.cssor the custom theme file. - Component not found: If using on‑demand, verify the resolver is correctly set up. Otherwise, import components explicitly.
- Icon not displaying: Make sure you have the icon font assets included. NutUI uses icon fonts; you may need to copy the font files or use a CDN.
For more details, visit the GitHub repository or the official site.