Create interactive, production-ready UI mockups and prototypes using NuxtJS 4 (Vue) or Next.js (React), TypeScript, and TailwindCSS v4...
Create polished, interactive UI mockups and prototypes using NuxtJS 4 (Vue) or Next.js (React) with TypeScript and TailwindCSS v4.
Rapid creation of production-quality mockups with:
NuxtJS 4 (Vue):
Next.js (React):
Common Features:
# Create NuxtJS 4 project (TypeScript enabled by default)
npx nuxi@latest init my-mockup
cd my-mockup
npm install
Install TailwindCSS v4 with Vite plugin:
npm install -D tailwindcss @tailwindcss/vite
Configure nuxt.config.ts:
// https://nuxt.com/docs/4.x/api/configuration/nuxt-config
import tailwindcss from '@tailwindcss/vite'
export default defineNuxtConfig({
devtools: { enabled: true },
typescript: {
strict: true,
typeCheck: true
},
// Auto-import components and composables
components: [
{
path: '~/components',
pathPrefix: false,
},
],
// App configuration
app: {
head: {
charset: 'utf-8',
viewport: 'width=device-width, initial-scale=1',
title: 'My Mockup',
meta: [
{ name: 'description', content: 'My mockup description' }
],
}
},
// Vite configuration with TailwindCSS v4
vite: {
plugins: [
tailwindcss()
],
css: {
devSourcemap: true
}
}
})
Create CSS file and import TailwindCSS (e.g., assets/css/main.css):
@import "tailwindcss";
Import CSS in app.vue or nuxt.config.ts:
// In nuxt.config.ts, add to the config:
export default defineNuxtConfig({
css: ['~/assets/css/main.css'],
// ... rest of config
})
npm run dev
# Opens http://localhost:3000
# Create Next.js project (uses recommended defaults with TypeScript and TailwindCSS)
npx create-next-app@latest my-mockup --yes
cd my-mockup
Or with custom options:
npx create-next-app@latest my-mockup
# Choose: TypeScript: Yes, TailwindCSS: Yes, App Router: Yes
Next.js includes TailwindCSS by default. To upgrade to v4:
npm install -D tailwindcss@next @tailwindcss/postcss@next
Update tailwind.config.ts:
import type { Config } from 'tailwindcss'
export default {
content: [
'./app/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
],
} satisfies Config
Update postcss.config.mjs:
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
Update app/globals.css:
@import "tailwindcss";
npm run dev
# Opens http://localhost:3000
Identify mockup requirements:
NuxtJS (Vue) auto-imports from:
my-mockup/
āāā components/
ā āāā layout/ # Header, Footer, Sidebar (auto-imported)
ā āāā ui/ # Button, Card, Modal, Input (auto-imported)
ā āāā sections/ # Hero, Features, Testimonials (auto-imported)
āāā pages/ # File-based routing (auto-routed)
āāā composables/ # Shared logic (auto-imported)
āāā layouts/ # Layout templates (default.vue, dashboard.vue)
āāā types/ # TypeScript interfaces
Next.js (React) structure:
my-mockup/
āāā app/
ā āāā layout.tsx # Root layout (required)
ā āāā page.tsx # Home page
ā āāā dashboard/
ā ā āāā page.tsx # /dashboard route
ā āāā globals.css # TailwindCSS imports
āāā components/
ā āāā layout/ # Header, Footer, Sidebar
ā āāā ui/ # Button, Card, Modal, Input
ā āāā sections/ # Hero, Features, Testimonials
āāā lib/ # Utilities and shared logic
āāā types/ # TypeScript interfaces
NuxtJS (Vue) Example: Type-safe Button Component (components/ui/Button.vue)
<script setup lang="ts">
// No need to import 'computed' - auto-imported by Nuxt
interface Props {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md'
})
const buttonClasses = computed(() => {
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50'
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
}
return `font-semibold rounded-lg transition ${variants[props.variant]} ${sizes[props.size]}`
})
</script>
<template>
<button :class="buttonClasses">
<slot />
</button>
</template>
Usage in pages/index.vue:
<template>
<div>
<!-- Auto-imported as UiButton from components/ui/Button.vue -->
<UiButton variant="primary" size="lg">
Get Started
</UiButton>
</div>
</template>
Next.js (React) Example: Type-safe Button Component (components/ui/Button.tsx)
import { ButtonHTMLAttributes } from 'react'
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
variant?: 'primary' | 'secondary' | 'outline'
size?: 'sm' | 'md' | 'lg'
children: React.ReactNode
}
export function Button({
variant = 'primary',
size = 'md',
children,
className = '',
...props
}: ButtonProps) {
const variants = {
primary: 'bg-blue-600 text-white hover:bg-blue-700',
secondary: 'bg-gray-600 text-white hover:bg-gray-700',
outline: 'border-2 border-blue-600 text-blue-600 hover:bg-blue-50'
}
const sizes = {
sm: 'px-3 py-1.5 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
}
const buttonClasses = `font-semibold rounded-lg transition ${variants[variant]} ${sizes[size]} ${className}`
return (
<button className={buttonClasses} {...props}>
{children}
</button>
)
}
Usage in app/page.tsx:
import { Button } from '@/components/ui/Button'
export default function Home() {
return (
<div>
<Button variant="primary" size="lg">
Get Started
</Button>
</div>
)
}
Use TailwindCSS breakpoints:
sm: (640px), md: (768px), lg: (1024px), xl: (1280px), 2xl: (1536px)NuxtJS (Vue) Responsive Grid Example:
<template>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Auto-imported as UiCard from components/ui/Card.vue -->
<UiCard v-for="item in items" :key="item.id" :data="item" />
</div>
</template>
<script setup lang="ts">
const items = ref([...])
</script>
Next.js (React) Responsive Grid Example:
import { Card } from '@/components/ui/Card'
export default function FeaturesSection() {
const items = [
{ id: 1, title: 'Feature 1', description: '...' },
// ...
]
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{items.map((item) => (
<Card key={item.id} data={item} />
))}
</div>
)
}
NuxtJS (Vue) Composable Pattern (composables/useModal.ts):
// Auto-imported by Nuxt - no need to import 'ref'
export function useModal() {
const isOpen = ref(false)
const open = () => { isOpen.value = true }
const close = () => { isOpen.value = false }
return { isOpen, open, close }
}
Usage in any Vue component:
<script setup lang="ts">
// Auto-imported - no import statement needed
const { isOpen, open, close } = useModal()
</script>
Next.js (React) Custom Hook Pattern (lib/hooks/useModal.ts):
import { useState } from 'react'
export function useModal() {
const [isOpen, setIsOpen] = useState(false)
const open = () => setIsOpen(true)
const close = () => setIsOpen(false)
return { isOpen, open, close }
}
Usage in any React component:
'use client' // Mark as Client Component for interactivity
import { useModal } from '@/lib/hooks/useModal'
export default function MyComponent() {
const { isOpen, open, close } = useModal()
return (
<div>
<button onClick={open}>Open Modal</button>
{isOpen && <Modal onClose={close} />}
</div>
)
}
NuxtJS:
npm run build # Build for production (.output/)
npm run preview # Preview production build
npm run generate # Generate static site (SSG)
Next.js:
npm run build # Build for production (.next/)
npm run start # Start production server (SSR)
# For static export (SSG), add to next.config.js:
# output: 'export'
Create layout (layouts/default.vue):
<template>
<div class="min-h-screen flex flex-col">
<LayoutHeader />
<main class="flex-1">
<slot />
</main>
<LayoutFooter />
</div>
</template>
Create page (pages/index.vue):
<template>
<div>
<SectionsHero />
<SectionsFeatures />
</div>
</template>
Create layout (app/layout.tsx):
import { Header } from '@/components/layout/Header'
import { Footer } from '@/components/layout/Footer'
import './globals.css'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className="min-h-screen flex flex-col">
<Header />
<main className="flex-1">{children}</main>
<Footer />
</body>
</html>
)
}
Create page (app/page.tsx):
import { Hero } from '@/components/sections/Hero'
import { Features } from '@/components/sections/Features'
export default function Home() {
return (
<div>
<Hero />
<Features />
</div>
)
}
Create dashboard layout (layouts/dashboard.vue):
<template>
<div class="flex h-screen bg-gray-100">
<LayoutSidebar class="w-64 bg-white shadow-lg" />
<div class="flex-1 flex flex-col">
<LayoutTopBar class="bg-white shadow-sm" />
<main class="flex-1 overflow-y-auto p-6">
<slot />
</main>
</div>
</div>
</template>
Use in page (pages/dashboard/index.vue):
<script setup lang="ts">
definePageMeta({
layout: 'dashboard'
})
</script>
<template>
<div>
<!-- Dashboard content -->
</div>
</template>
Create dashboard layout (app/dashboard/layout.tsx):
import { Sidebar } from '@/components/layout/Sidebar'
import { TopBar } from '@/components/layout/TopBar'
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex h-screen bg-gray-100">
<Sidebar className="w-64 bg-white shadow-lg" />
<div className="flex-1 flex flex-col">
<TopBar className="bg-white shadow-sm" />
<main className="flex-1 overflow-y-auto p-6">
{children}
</main>
</div>
</div>
)
}
Use in page (app/dashboard/page.tsx):
export default function DashboardPage() {
return (
<div>
{/* Dashboard content */}
</div>
)
}
blue-600, Secondary: gray-600green-600, Warning: yellow-600, Error: red-600gray-100 to gray-900Use consistent scale: p-1 (4px), p-2 (8px), p-4 (16px), p-6 (24px), p-8 (32px)
text-4xl, text-3xl, text-2xl, text-xltext-base (16px)font-normal, font-medium, font-semibold, font-boldFor detailed implementations:
Helper scripts available in scripts/ (Bash and PowerShell versions):
create-component.sh / .ps1 - Generate components with TypeScript boilerplate
# Linux/macOS - Vue component
./scripts/create-component.sh ComponentName ui vue
# Linux/macOS - React component
./scripts/create-component.sh ComponentName ui react
# Windows - Vue component
.\scripts\create-component.ps1 -ComponentName ComponentName -Type ui -Framework vue
# Windows - React component
.\scripts\create-component.ps1 -ComponentName ComponentName -Type ui -Framework react
build-deploy.sh / .ps1 - Build and prepare for deployment
# Linux/macOS
./scripts/build-deploy.sh
# Windows
.\scripts\build-deploy.ps1
NuxtJS CLI commands:
npx nuxi add component ComponentName # Add new component
npx nuxi add page pageName # Add new page
npx nuxi add layout layoutName # Add new layout
Next.js CLI commands:
# No built-in CLI for components, use scripts above
# Or manually create files in app/ or components/
TailwindCSS v4 not working:
@import "tailwindcss"; in your CSS file@tailwindcss/vite plugin is in vite.plugins arraynpx nuxi prepare to regenerate typesTypeScript errors:
npx nuxi prepare to generate typesAuto-imports not working:
rm -rf .nuxt
npx nuxi prepare
npm run dev
HMR issues:
rm -rf .nuxt node_modules/.cache
npm run dev
Large bundle size:
defineAsyncComponent(() => import('./Component.vue'))npx nuxi analyzeTailwindCSS not working:
tailwind.config.ts has correct content paths@import "tailwindcss"; in app/globals.css.next folder: rm -rf .next && npm run devTypeScript errors:
npm run build to see full type checkingServer vs Client Components:
'use client' directive at top of file for interactive componentsBuild errors:
rm -rf .next node_modules/.cache
npm install
npm run build
Large bundle size:
const Component = dynamic(() => import('./Component'))npm run build (shows bundle sizes)useHead() and useSeoMeta() for meta tagsgenerateMetadata() for dynamic SEO tags<Image> component for automatic optimization