Skip to content
WingoUI
ComponentsTemplatesDocsPricingAI agentsBlogChangelogContact
Components
Buttons & Actions
ButtonButton GroupCopy ButtonDelete ButtonTheme ToggleToggleToggle Group
Inputs
Avatar UploadCalendarCheckboxColor PickerComboboxCountry SelectCron PickerCurrency InputDate PickerDate Range PickerDate Time PickerDuration PickerEmail InputEmoji PickerFile UploadFilter BuilderInputInput GroupListboxMasked InputMention InputMonth PickerMulti SelectNumber InputOTP InputPassword InputPhone InputRadio GroupRatingSearch InputSegmented ControlSelectSignature PadSliderSwitchTag InputTextareaTime PickerTimezone SelectTree Select
Forms
Address InputBooking WidgetCSV ImportFieldForm WizardInline EditInvoice line itemsRich Text EditorSchema Builder
Navigation
Back To TopBounce SidebarBreadcrumbCommand MenuGlobal SearchGooey NavHook SidebarNav MenuNav TabsPaginationProximity SidebarScroll ProgressSidebarStepsTable Of ContentsTabsTopbarUser MenuWorkspace Switcher
Overlays
Alert DialogContext MenuDialogDrawerDropdown MenuFamily DrawerHover CardPopoverShare DialogSheetShortcuts DialogTooltipTour
Feedback
AlertBannerCookie ConsentEmoji ReactionEmpty StateNotification BellNotification CenterProgressRoute ProgressSetup ChecklistSkeletonSpinnerToast
Data Display
Approval FlowAvatarBadgeCalendar ViewCode BlockComment ThreadContact cardCountdownDescription ListDiff ViewerFile ListFile managerFolderGanttJSON ViewerKbdLog ViewerMarkdownPresenceSchedulerTagTimelineTree View
Tables & Lists
Audit LogData TableDeal PipelineEditable GridKanbanListMembers tablePermissions MatrixTableTask ListVirtual List
Charts & Stats
Animated CounterArea chartBar chartDonut chartFunnel chartGaugeGitHub ActivityHeatmap calendarLeaderboardLine chartRadar chartRadial barSparklineStat CardTrackerTrend badge
Layout
AccordionApp ShellCardCollapsibleDetail LayoutEntity HeaderPage HeaderScroll AreaSeparatorSettings LayoutSplit ViewToolbar
Media
Audio PlayerCarouselCSV ViewerDOCX EditorDOCX ViewerExcel ViewerFile SystemImage compareImage CropperImage GalleryImage LightboxLocation MapPDF ViewerPowerPoint ViewerQR CodeStep PlayerVideo Player
AI Kit
Agent Status CardChat MessageChat ThreadChat WidgetConversation ListFluid OrbGrid RevealMatrix OrbModel PickerPrompt InputReasoning IndicatorRule BuilderSuggestion ChipsTool Call TimelineUsage MeterVoice InputWorkflow Canvas
Text & Effects
Background PatternGravity LettersMarqueeTerminalText AnimateText Rotate
Mobile
Action SheetBottom Tab BarFABHaptic ButtonHero HeaderInstall PromptLarge Title HeaderNotification BannerOnboarding CarouselPicker WheelPull To RefreshSlide To ConfirmSticky CTA BarSwipe DeckSwipeable Tabs
Commerce
Cart drawerCategory pickerCheckout stepsCoupon inputFavorite buttonFilter panelListing cardListing gridOrder trackerPricePrice rangePricing tableProduct galleryQuantity stepperReviewsSeller card
Marketing Sections
App Download SectionBlog Cards SectionCTA SectionFAQ SectionFeature GridFeature ShowcaseHero SectionIntegrations SectionLogo CloudMarketing FooterMarketing HeaderMarketing SectionNewsletter SectionPricing SectionStats SectionSteps SectionTeam SectionTestimonials SectionTimeline SectionVideo Section
Blocks
Admin Dashboard PageAdmin ShellAI Chat PageAPI Keys PageAutomation Builder PageCheckout PageCRM Contact PageCRM Pipeline PageError PagesInbox PageInvoice PageKPI DashboardListing Detail PageLogin PageMarketplace Home PageMarketplace Search PageMarketplace ShellMobile App HomeOnboarding FlowOrder History PagePost Listing WizardPricing PageProfile PageRegister PageSettings PageUsers Admin Page
Templates
About PageAcademy PageAccessibility PageAffiliate PageAgency PageAI Product PageAppointment Booking PageBlog PageBlog Post PageBug Bounty PageCareers PageChangelog PageCommunity PageCompare PageContact PageCustomer Story PageCustomers PageDemo Booking PageDevelopers PageDocs Home PageDownload PageEnterprise PageEvents PageExperts PageFAQ PageFeature PageGlossary PageHelp Article PageHelp Center PageHome Services PageImpact PageIntegration Detail PageIntegrations PageJob Detail PageLanding PageLaunch PageLegal PageManifesto PagePortfolio PagePress PageProduct Tour PagePromo PageReal Estate PageReferral PageResearch Report PageRestaurant PageRoadmap PageROI Calculator PageSecurity PageSolutions PageStartups PageStatus PageStorefront PageSwitch PageTemplate Gallery PageTravel PageWaitlist PageYear in Review Page
Hooks & Utilities
AI ChatChart kitCommerceDate utilsFile exportFile typesFiltersForm validationHapticsInvoicingMotion presetsNavigationNumber formatRelative timeRomanian locationsSpreadsheetText matchTonesUIProvideruseClipboarduseConsentuseControllableStateuseCooldownuseDebounceuseKeyboardInsetuseLocaleuseLongPressuseMediaControlsuseMediaQueryuseOverflowItemsusePersistedStateuseScrollEdgesuseScrollStateuseSelectionuseShortcutuseStickToBottom
GalleryTheme

Templates

Portfolio Page

Templates

Portfolio Page React template for Tailwind CSS v4

Manifesto PagePress Page
Live props
State
available
defaultProject
Layout
layout
showExperience
showServices
showTestimonials
showWriting
showContact
header
footer
Content
currency
locale
Appearance
tone
ctaTone
radius
background
backgroundColor
bandBackground
bandColor
Behavior
stickyBar
animateIn
Code
import { PortfolioPage } from "@/components/templates/portfolio-page"

<PortfolioPage />
  1. Home
  2. Templates
  3. Portfolio Page
Pro

Portfolio Page React template for Tailwind CSS v4

v1.0.0·Added in 1.14.0

A React portfolio template for designers and developers: hero, case studies in a sheet, experience, priced services, writing and contact.

Start a project and Hire me glide down to the contact band and put the cursor in the name field; See my work glides to the projects (instantly under reduced motion). Every project card is one button: it lifts on hover and opens the case study in a sheet from the right (a bottom sheet you can swipe down on phones) with the problem, the numbered process, outcome numbers and a swipeable image strip that arrow keys scroll once it has focus. The arrow buttons in the sheet move to the previous or next project, Escape closes it and focus returns to the card, and Start a similar project closes it and jumps to the form. Book this on a service picks that service in the form. Copy email copies the address and says Copied. The form checks the name, the email and the message, focuses the first problem, spins on Send message and then thanks the sender. On phones a Hire me bar slides up once the hero scrolls away and hides again at the contact band.

Portfolio Page is a Pro React template in the React website templates category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add portfolio-page, together with Marketing Section, Marketing Header, Marketing Footer, Timeline Section, Testimonials Section and 14 more and the npm packages lucide-react and motion.

Built with

The sections this page is made of. In the full-screen preview, hold Alt to outline each one on the page.

  • Marketing Sectionmarketing-section
  • Marketing Headermarketing-header
  • Marketing Footermarketing-footer
  • Timeline Sectiontimeline-section
  • Testimonials Sectiontestimonials-section
  • Blog Cards Sectionblog-cards-section

Add to a project

Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup

npx wingo-ui@latest add portfolio-page

Portfolio Page is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans

Usage

Import PortfolioPage from @/components/templates/portfolio-page.

// app/page.tsx
"use client"

import Link from "next/link"
import { PortfolioPage, type PortfolioContactValues } from "@/components/templates/portfolio-page"

async function sendMessage(values: PortfolioContactValues) {
  await fetch("/api/contact", { method: "POST", body: JSON.stringify(values) })
}

export default function HomeRoute() {
  return <PortfolioPage linkComponent={Link} layout="grid" onContact={sendMessage} />
}

Props

  • ownerdefault "PORTFOLIO_OWNER"
    PortfolioOwner

    { name, role, location?, avatar?, email, socials? }: the person in the header, hero, contact band, sticky bar and footer. Without an avatar url the initials show.

  • projectsdefault "PORTFOLIO_PROJECTS"
    PortfolioProject[]

    { slug, title, client, summary, year, role, duration?, tags, color, cover, image?, problem, process, outcome, metrics, gallery?, href? }. cover is a drawn picture (app, mobile, dashboard, brand, flow, type) tinted with color; image replaces it with a photo.

  • experiencedefault "PORTFOLIO_EXPERIENCE"
    PortfolioExperience[]

    { company, role, start, end?, location?, description, highlight? } roles for the timeline; a role without end is the current one and gets the Now pill.

  • servicesdefault "PORTFOLIO_SERVICES"
    PortfolioService[]

    { id, title, description, price, unit?, duration?, features, icon?, featured? }. The featured one is ringed with a Most booked badge; the services are also the choices in the contact form.

  • testimonialsdefault "PORTFOLIO_TESTIMONIALS"
    Testimonial[]

    Client quotes for the testimonials grid (the TestimonialsSection shape).

  • postsdefault "PORTFOLIO_POSTS"
    PortfolioPost[]

    Posts for the writing list (the BlogCardsSection post shape); they link to /blog/<slug>.

  • postCategoriesdefault "PORTFOLIO_POST_CATEGORIES"
    Record<string, BlogCardsCategory>

    The categories the posts point to: { label, icon?, color? }.

  • factsdefault "PORTFOLIO_FACTS"
    PortfolioFact[]

    { value, label } key facts under the hero actions; an empty array hides them.

  • labels
    Partial<PortfolioPageLabels>

    Every string on the page, including the hero headline and bio, the section headings, the form and its errors; a few are functions (period, basedIn, successDescription).

  • availabledefault true
    boolean

    Open for work: a pulsing green pill reading Available for new projects. false shows an amber Booked through January 2027 (labels.unavailable) in the hero, the contact band and the sticky bar.

  • layoutdefault "grid"
    "grid" | "list"

    The work section: grid shows two large cards per row; list shows one wide card per row with the cover beside the text, alternating sides, and two outcome numbers. Both are one column on phones.

  • project
    string

    The slug of the open case study (controlled); "" closes the sheet.

  • defaultProjectdefault ""
    string

    The case study open on first render (uncontrolled), e.g. northwind-planner.

  • onProjectChange
    (slug: string) => void

    Fires with the slug when a case study opens or changes, and with "" when it closes.

  • onContact
    (values: PortfolioContactValues) => void | Promise<unknown>

    Runs on Send message with { name, email, projectType, message }. A returned promise keeps the button loading; a rejection keeps the form and shows labels.failed. Without it the page pretends for 900 ms, then thanks the sender.

  • currencydefault "USD"
    string

    ISO currency of the service prices.

  • localedefault ""
    string

    Intl locale for prices and post dates; empty uses the UIProvider's, else en-US.

  • tonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Accents: the process step numbers, the featured service ring and badge, the timeline's current role and the testimonial metrics. A non-neutral tone also tints the page glow.

  • ctaTonedefault "neutral"
    "neutral" | "brand" | "success" | "warning" | "danger" | "info"

    Hire me, Start a project, Book this on the featured service and Send message.

  • radiusdefault "lg"
    "none" | "sm" | "md" | "lg"

    Corners of the project and service cards: 0 / 12 / 16 / 24px; the covers inside follow.

  • backgrounddefault "glow"
    "plain" | "soft" | "glow" | "aurora" | "grid" | "dots"

    The main background: plain is the bare page color; the others tint the page and add a glow, an aurora, a grid or dots over the first screen.

  • backgroundColordefault "oklch(0.62 0.14 275)"
    string

    The tint and glow color of the main background; left at the default it follows tone when tone is not neutral.

  • bandBackgrounddefault "muted"
    "muted" | "tinted" | "gradient" | "dots"

    The secondary background of the experience, testimonials and contact bands. Cards and fields inside are lifted one step above it.

  • bandColordefault "oklch(0.62 0.14 275)"
    string

    The tint of tinted and gradient bands; left at the default it follows backgroundColor.

  • showExperiencedefault true
    boolean

    The experience timeline (and its link in the header).

  • showServicesdefault true
    boolean

    The services with starting prices; off also removes them from the form's choices.

  • showTestimonialsdefault true
    boolean

    The testimonials band.

  • showWritingdefault true
    boolean

    The writing list.

  • showContactdefault true
    boolean

    The contact band with the email and the form. Off, Hire me opens a mailto link instead.

  • stickyBardefault true
    boolean

    Phones only: a Hire me bar pinned to the bottom (above the home indicator) once the hero scrolls away, hidden again at the contact band and while a case study is open.

  • headerdefault true
    boolean

    The sticky header with the name, the section links and Hire me. Off when a site layout already has one.

  • footerdefault true
    boolean

    The simple footer with the section links and the copyright line.

  • headerProps
    Partial<MarketingHeaderProps>

    Anything for MarketingHeader (layout, variant, items...); wins over the page's own values.

  • footerProps
    Partial<MarketingFooterProps>

    Anything for MarketingFooter (variant, columns, socials...).

  • experienceProps
    Partial<TimelineSectionProps>

    Anything for the experience TimelineSection (layout, card, marker...); wins over the page's values.

  • testimonialsProps
    Partial<TestimonialsSectionProps>

    Anything for the TestimonialsSection (layout, columns, card...).

  • writingProps
    Partial<BlogCardsSectionProps>

    Anything for the writing BlogCardsSection (layout, limit, viewAllHref, basePath...).

  • animateIndefault true
    boolean

    The hero rises in once when the page mounts on the client; never during hydration, under reduced motion or in thumbnails.

  • linkComponent
    (props: LinkComponentProps) => ReactNode

    The router link for the header, footer and post links (next/link in an app).

  • className
    string

    Classes for the root.

  • classNames
    Partial<Record<PortfolioPageSlot, string>>

    Classes for header, hero, heroMedia, work, project, cover, caseStudy, experience, services, service, testimonials, writing, contact, form, stickyBar and footer.

Parts

  • PORTFOLIO_PAGE_SAMPLE

    constant

    All the sample content in one object: owner, facts, projects, experience, services, testimonials, posts and postCategories.

  • PORTFOLIO_OWNER

    constant

    Emma Carter, product designer in New York, with an email and three socials.

  • PORTFOLIO_FACTS

    constant

    Three hero facts: years, products shipped, startups.

  • PORTFOLIO_PROJECTS

    constant

    Four case studies for Northwind, Umbrella Health, Globex and Wayne Studio.

  • PORTFOLIO_EXPERIENCE

    constant

    Four roles from 2015 to today, newest first.

  • PORTFOLIO_SERVICES

    constant

    A design sprint, a design system (featured) and a monthly design partner retainer.

  • PORTFOLIO_TESTIMONIALS

    constant

    Three client quotes with ratings and metrics.

  • PORTFOLIO_POSTS

    constant

    Four essays from 2026.

  • PORTFOLIO_POST_CATEGORIES

    constant

    Process, design systems, research, data and career, with icons and colors.

  • DEFAULT_PORTFOLIO_PAGE_LABELS

    constant

    The English strings.

  • PortfolioPageProps

    type

    Props of PortfolioPage, including the attributes of the root element.

  • PortfolioPageLabels

    type

    Every string of the page.

  • PortfolioPageLayout

    type

    "grid" | "list".

  • PortfolioPageRadius

    type

    "none" | "sm" | "md" | "lg".

  • PortfolioPageSlot

    type

    The keys of classNames.

  • PortfolioContactValues

    type

    What onContact receives: { name, email, projectType, message }.

  • PortfolioOwner

    type

    { name, role, location?, avatar?, email, socials? }.

  • PortfolioSocial

    type

    { label, href, icon? }; label is the link's accessible name.

  • PortfolioFact

    type

    { value, label }.

  • PortfolioProject

    type

    One case study, from the card fields to the problem, process, outcome, metrics and gallery.

  • PortfolioCoverKind

    type

    "app" | "mobile" | "dashboard" | "brand" | "flow" | "type": the drawn covers.

  • PortfolioStep

    type

    { title, description }: one step of a case study's process.

  • PortfolioMetric

    type

    { value, label }: one outcome number.

  • PortfolioFrame

    type

    { cover, caption, image? }: one picture of the case study's image strip.

  • PortfolioExperience

    type

    One role of the timeline.

  • PortfolioService

    type

    One service card with its starting price.

  • PortfolioPost

    type

    A post of the writing list (BlogCardsPost).

Examples

Open

List layout

layout="list": one wide card per project with the cover beside the text, alternating sides, and two outcome numbers on each.

Open

A case study open

defaultProject opens the Northwind case study in the sheet: problem, process, outcome numbers and the image strip. The arrows step through the projects.

Open

Booked, with brand accents

available={false} turns the pill amber (Booked through January 2027); tone and ctaTone brand on an aurora page with tinted bands.

Open

A developer, work and contact only

Liam Brooks, a design engineer in Austin: his own owner and labels, square-ish cards, no services, testimonials or writing, on a grid background.

Open

When sending fails

onContact rejects: after the spinner the form keeps everything and shows the error above Send message.

Open

Inside a site layout

No header, footer or sticky bar, on a soft page: the sections alone.

Dependencies

lucide-reactmotionmarketing-sectionmarketing-headermarketing-footertimeline-sectiontestimonials-sectionblog-cards-sectionavatarbadgebuttoncopy-buttoninputradio-groupsheettextareause-controllable-statenavigationmotiontonesui-config

Files

  • components/templates/portfolio-page.tsx
  • components/templates/portfolio-page-data.ts

FAQ

Is the React Portfolio Page template free?

The preview, props and docs are free; copying the source needs a Pro plan. See plans.

How do I install Portfolio Page?

Run npx wingo-ui@latest add portfolio-page. It also adds the Wingo UI items it builds on (Marketing Section, Marketing Header, Marketing Footer, Timeline Section, Testimonials Section and 14 more) and the npm packages lucide-react and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/portfolio-page.

How does Portfolio Page behave on phones?

Every project card is one button: it lifts on hover and opens the case study in a sheet from the right (a bottom sheet you can swipe down on phones) with the problem, the numbered process, outcome numbers and a swipeable image strip that arrow keys scroll once it has focus.

See also

About PageContact PageTimeline SectionTestimonials SectionSheet

Related guides

  • Nextjs Agency Template That Turns Visitors Into Briefs

    Build it: screens and apps · Nov 6, 2026 · 8 min read

  • Nextjs Portfolio Template That Wins Clients

    Build it: screens and apps · Nov 6, 2026 · 9 min read

PreviousManifesto PageNext Press Page