Nuxt UI Portfolio Template
repository·main·Indexed 42 days ago
https://github.com/nuxt-ui-templates/portfolioA professional portfolio template built with Nuxt and Nuxt UI, featuring a blog powered by Nuxt Content. Includes pre-configured schemas for projects, speaking engagements, and testimonials, with customizable theme colors and profile settings via app.config.ts.
What's inside Nuxt UI Portfolio Template
- To automate dependency updates, install the Renovate GitHub app on your repository.
Run the Development Server
mainStart the local development server to view and iterate on your portfolio. The application will be available at
http://localhost:3000.pnpm devSetup and Install Dependencies
mainAfter scaffolding your project, navigate to the project directory and install the necessary dependencies using
pnpm.pnpm installDeploy to Vercel
mainYou can deploy this template directly to Vercel by cloning the repository through the Vercel deployment interface.Quick Start with the Nuxt Portfolio Template
mainTo create a new project using the Nuxt Portfolio template, use the
nuxiCLI vianpm create nuxt. This will scaffold a new Nuxt application pre-configured with the portfolio template and Nuxt UI.npm create nuxt@latest -- -t ui/portfolioBuild and Preview for Production
mainTo prepare your portfolio for deployment, build the application for production. You can also preview the production build locally to ensure everything works correctly before deploying.
# Build the application pnpm build # Locally preview the production build pnpm previewStructure of the Home Page (index.vue)
mainThe home page (
app/pages/index.vue) is a data-driven route that fetches content from a collection namedindexusing Nuxt Content/Collections. It usesuseAsyncDatato retrieve the page data anduseSeoMetato dynamically set SEO metadata based on the fetched content.If the page data is not found, it throws a fatal 404 error. The template renders a series of landing components that consume the fetched
pagedata:LandingHeroLandingAboutLandingWorkExperienceLandingBlogLandingTestimonialsLandingFAQ
<template> <UPage v-if="page"> <LandingHero :page /> <UPageSection :ui="{ container: 'pt-0! lg:grid lg:grid-cols-2 lg:gap-8' }"> <LandingAbout :page /> <LandingWorkExperience :page /> </UPageSection> <LandingBlog :page /> <LandingTestimonials :page /> <LandingFAQ :page /> </UPage> </template>Root application structure and layout in app.vue
mainThe root component
app.vuedefines the global application shell. It uses<UApp>as the top-level wrapper, followed by<NuxtLayout>for page layouts and<UMain>to wrap the<NuxtPage />content. A global search component<LazyUContentSearch>is rendered inside a<ClientOnly>block to ensure it only runs on the client side, preventing hydration mismatches.Key global behaviors include:
- Dynamic Theme Color: The
theme-colormeta tag is dynamically updated based on the currentuseColorMode()value. - SEO Configuration: A global
titleTemplateis set to%s - Nuxt Portfolio TemplateviauseSeoMeta. - Data Fetching: Navigation and search data are fetched at the root level using
useAsyncDataanduseLazyAsyncDatato populate the global search functionality.
<template> <UApp> <NuxtLayout> <UMain class="relative"> <NuxtPage /> </UMain> </NuxtLayout> <ClientOnly> <LazyUContentSearch :files="files" :navigation="navigation" :links="navLinks" :fuse="{ resultLimit: 42 }" /> </ClientOnly> </UApp> </template>- Dynamic Theme Color: The
Implement a blog listing page using Nuxt Content Collections
mainTo create a blog listing page, use
useAsyncDatato fetch two distinct datasets from the content collections:- The Page Metadata: Fetch the specific page configuration (SEO, title, description) using
queryCollection('pages').path('/blog').first(). - The Blog Posts: Fetch all posts from the
blogcollection, ordered by date in descending order usingqueryCollection('blog').order('date', 'DESC').all().
If either the page or the posts are not found, you should throw a fatal 404 error using
createErrorto ensure the Nuxt application handles the missing content correctly.// Fetch page metadata const { data: page } = await useAsyncData('blog-page', () => { return queryCollection('pages').path('/blog').first() }) // Fetch blog posts const { data: posts } = await useAsyncData('blogs', () => queryCollection('blog').order('date', 'DESC').all() ) // Error handling if content is missing if (!page.value || !posts.value) { throw createError({ statusCode: 404, statusMessage: 'Page not found', fatal: true }) }- The Page Metadata: Fetch the specific page configuration (SEO, title, description) using
Configure SEO for the blog listing page
mainThe blog listing page dynamically sets SEO metadata based on the fetched page content. It prioritizes
seo.titleandseo.descriptionfrom the content collection, falling back to the standardtitleanddescriptionfields if the SEO-specific fields are absent.Use
useSeoMetato apply these values to the document and Open Graph tags, anddefineOgImageto set the social sharing image.const title = page.value?.seo?.title || page.value?.title const description = page.value?.seo?.description || page.value?.description useSeoMeta({ title, ogTitle: title, description, ogDescription: description }) defineOgImage('Portfolio', { title, description })Configure ESLint for the Nuxt Portfolio Template
mainThe project uses a flat configuration format via
eslint.config.mjs. It leverageswithNuxtto integrate Nuxt-specific linting rules andeslint-plugin-better-tailwindcssto enforce Tailwind CSS best practices.To customize linting, you can modify the
rulesobject or thesettingsfor the Tailwind plugin. Note that the configuration is wrapped inwithNuxt(), which provides the base Nuxt environment settings.import withNuxt from './.nuxt/eslint.config.mjs' import betterTailwindcss from 'eslint-plugin-better-tailwindcss' import { getDefaultAttributes } from 'eslint-plugin-better-tailwindcss/api/defaults' export default withNuxt( { rules: { '@typescript-eslint/no-explicit-any': 'off' } }, betterTailwindcss.configs['correctness-error'], { settings: { 'better-tailwindcss': { entryPoint: 'app/assets/css/main.css', attributes: [ ...getDefaultAttributes(), ['^v-bind:ui$', [{ match: 'objectValues' }]] ] } } } )Configure UI colors and Page Hero styling
mainThe
uikey inapp.config.tsallows you to customize the theme colors and the layout of the Page Hero component via Tailwind CSS classes.Colors
primary: The primary brand color (e.g.,'blue').neutral: The neutral color scale used for backgrounds and text.
Page Hero Slots
You can override the Tailwind classes for specific slots in the
pageHerocomponent:container: Padding and layout for the hero section.title: Styling for the hero title.description: Styling for the hero description text.
export default defineAppConfig({ ui: { colors: { primary: 'blue', neutral: 'neutral' }, pageHero: { slots: { container: 'py-18 sm:py-24 lg:py-32', title: 'mx-auto max-w-xl text-pretty text-3xl sm:text-4xl lg:text-5xl', description: 'mt-2 text-md mx-auto max-w-2xl text-pretty sm:text-md text-muted' } } } })