A single-page portfolio template built with React and Vite, featuring smooth animations, section-based layout, and mock data so you can run and deploy without any backend. Use it as a learning resource, a starter for your own portfolio, or a reference for React patterns, HOCs, and Framer Motion.
- Live Demo: https://portfolio-ui-7.vercel.app/
- Overview
- Tech Stack & Keywords
- Project Structure
- Getting Started
- Environment Variables
- How the Project Works
- Components & Reuse
- Data Layer & API
- Features & Functionalities
- Backend & Deployment
- Code Snippets
- Keywords
- Conclusion
- License
This is a React + Vite frontend project (not Create React App). It is a one-page portfolio with sections: Header, About, Work (portfolio with filters), Skills & Experience, Testimonials, and Contact. All content is driven by mock data in src/mockData.js, so no CMS or API is required to run or deploy. The app uses Framer Motion for animations, SASS for styling, and a small wrapper layer (HOCs) for shared layout and section IDs used by the navbar and navigation dots.
| Category | Technologies / Terms |
|---|---|
| Framework | React 19, Vite 7 |
| Styling | SASS (SCSS) |
| Animation | Framer Motion |
| Icons | react-icons |
| Data | In-memory mock (no backend required) |
| Linting | ESLint (Airbnb + React) |
| Deploy | Vercel (output: dist) |
Keywords: portfolio, React, Vite, single-page, mock data, Framer Motion, SASS, HOC, section navigation, contact form, testimonials, skills, works filter.
portfolio-ui-7/
βββ index.html # Vite entry HTML (root)
βββ vite.config.js # Vite + React plugin config
βββ vercel.json # Vercel: framework, buildCommand, outputDirectory
βββ package.json
βββ .env.example # Optional env vars (see Environment Variables)
βββ public/
β βββ favicon.ico
β βββ robots.txt
βββ src/
β βββ index.js # React root (createRoot, StrictMode)
β βββ index.css # Global styles
β βββ App.js # Main app: Navbar + section containers
β βββ App.scss
β βββ client.js # Data client: mock fetch/create, urlFor (no Sanity in bundle)
β βββ mockData.js # All section data (abouts, works, skills, experiences, testimonials, brands)
β βββ constants/
β β βββ index.js
β β βββ images.js # Static asset imports (logos, icons, placeholders)
β βββ components/ # Reusable UI
β β βββ Navbar
β β βββ SocialMedia
β β βββ NavigationDots
β βββ container/ # Section screens (wrapped with AppWrap / MotionWrap)
β β βββ Header
β β βββ About
β β βββ Work
β β βββ Skills
β β βββ Testimonial
β β βββ Footer
β βββ wrapper/
β βββ AppWrap.js # HOC: layout + SocialMedia + NavigationDots + section id
β βββ MotionWrap.js # HOC: Framer Motion scroll-in animation
β βββ index.js
βββ backend_sanity_portfolio/ # Optional Sanity CMS schemas (not used by app at runtime)- Node.js >= 20 and < 25 (see
enginesinpackage.json)
# Clone the repository (if needed)
git clone <your-repo-url>
cd portfolio-ui-7
# Install dependencies
npm install
# Start development server (Vite)
npm run devOpen http://localhost:5173 (or the URL Vite prints).
# Production build (output in dist/)
npm run build
# Preview production build locally
npm run previewnpm run lint # Check only
npm run lint:fix # Auto-fix where possibleThe app runs without any environment variables. All content comes from src/mockData.js.
If you want to document or later switch to a real backend (e.g. Sanity), you can use a .env file. Copy from .env.example:
cp .env.example .env.env.example documents optional variables (currently unused in code):
| Variable | Purpose |
|---|---|
VITE_USE_MOCK_DATA |
Force mock data (e.g. true) |
VITE_USE_SANITY |
Opt-in to Sanity in production (e.g. true) |
VITE_SANITY_PROJECT_ID |
Sanity project ID (for future use) |
VITE_SANITY_TOKEN |
Sanity token (for future use) |
Important: Only variables prefixed with VITE_ are exposed to the client bundle in Vite. Do not put secrets in VITE_* if they must stay server-only.
For Vercel: you do not need to set any of these for the current mock-only setup. Build command is npm run build, output directory is dist (see vercel.json).
-
Entry:
index.htmlloads/src/index.jsas a module.index.jsrenders<App />into#rootwithReactDOM.createRootandStrictMode. -
App:
App.jsrenders a fixedNavbarand then the section components in order:Header,About,Work,Skills,Testimonial,Footer. Each section is a container that may be wrapped byAppWrapand/orMotionWrap. -
Navigation: The Navbar and
NavigationDotsuse anchor links#home,#about,#work,#skills,#testimonial,#contact. These IDs are set byAppWrap(Component, idName, classNames)on the wrapper div. -
Data: Containers that need data call
client.fetch(query)(orclient.create(...)in Footer). The client insrc/client.jsis a mock implementation that resolves with data frommockData.js; there are no HTTP requests.urlFor(source)is a no-op that returnssource(for compatibility with a possible future Sanity image helper). -
Styling: Global and section-specific SCSS are imported in
App.jsand in each container/component. BEM-like class names (app__section-element) are used for scoping. -
Animations: Framer Motion is used for scroll-in and hover effects.
MotionWrapadds a common scroll-in animation to a section content.
- Desktop: logo + horizontal links (
#home,#about,#work,#skills,#contact). - Mobile: hamburger menu that toggles a slide-in list of the same links.
- Reuse: Import
Navbar, place at top of your layout. Ensure your section wrapper divs have matchingids (e.g.id="about").
- Renders Twitter, Facebook, Instagram icons (no links by default).
- Reuse: Replace icons or add
hrefin each wrapper. Used insideAppWrapon the right side of sections.
- Vertical dots for
home,about,work,skills,testimonial,contact. Active dot is highlighted viaactiveprop. - Reuse:
<NavigationDots active={currentSectionId} />. Ensure section IDs match the dot keys.
- Wraps a section with: outer div
id={idName},SocialMedia, inner wrapper withComponentand a copyright block, andNavigationDots. - Reuse:
export default AppWrap(YourSection, 'section-id', 'optional-classNames');
- Wraps a component in a
motion.divwithwhileInView(e.g. slide-up + fade-in). - Reuse:
export default AppWrap(MotionWrap(YourSection, 'classNames'), 'id', 'wrapperClass');
- Each fetches its data (or uses static content) and renders one section. They are composed with
AppWrapand oftenMotionWrap. - Reuse: Copy a container folder (e.g.
About), change theclient.fetchquery or replace with local state, and register it inApp.jsand incontainer/index.js. Add a corresponding nav link and dot inNavbarandNavigationDots.
There are no external API endpoints. The app uses an in-memory data layer in src/client.js:
-
client.fetch(query)β Returns a Promise that resolves with mock arrays keyed by query content (e.g.abouts,works,skills,experiences,testimonials,brands). Query format is Sanity-style (*[_type == "abouts"]) but not executed; the client only checksquery.includes('abouts')etc. -
client.create(payload)β Returns a Promise that resolves with a fake{ _id }. Used by the contact form in Footer; no data is sent anywhere. -
urlFor(source)β Returnssourceunchanged. Kept so that components can pass image references that would later be resolved by a Sanity image URL builder if you plug one in.
Data lives in src/mockData.js: mockAbouts, mockWorks, mockSkills, mockExperiences, mockTestimonials, mockBrands. Edit this file to change portfolio content without touching the backend.
| Feature | Where | Description |
|---|---|---|
| Single-page navigation | Navbar, NavigationDots | Anchor links to sections by id. |
| Responsive navbar | Navbar | Hamburger menu on small screens; slide-in list. |
| Header | Header | Intro line, name, tags, profile image, floating tech circles. |
| About | About | Cards (title, description, image) from mock abouts. |
| Work / Portfolio | Work | Grid of projects; filter by All, UI/UX, Web App, Mobile App, React JS. Each item: image, title, description, tag, links (view / code). |
| Skills & Experience | Skills | List of skills (icon, name) and timeline of experiences (year, works with name, company); tooltips on work items via react-tooltip. |
| Testimonials | Testimonial | One testimonial at a time; prev/next buttons; brand logos below. |
| Contact | Footer | Email/phone links; form (name, email, message). Submit calls client.create() and shows a thank-you state (no real backend). |
| Animations | Framer Motion | Scroll-in and hover effects in sections and cards. |
| SEO | index.html | Meta title, description, author, keywords, Open Graph, Twitter Card, canonical URL. |
-
Backend: None required. The app is frontend-only and uses mock data. The optional
backend_sanity_portfolio/folder contains Sanity schemas for reference only; the running app does not call Sanity. -
Deployment (Vercel):
- Build:
npm run build - Output:
dist - Config:
vercel.jsonsetsframework,buildCommand, andoutputDirectory. No env vars are required for the current mock setup.
- Build:
import { client, urlFor } from "../../client";
useEffect(() => {
client.fetch('*[_type == "abouts"]').then((data) => {
setAbouts(data);
});
}, []);
// Render: urlFor(item.imgUrl) for image srcimport { AppWrap, MotionWrap } from '../../wrapper';
const MySection = () => ( /* ... */ );
export default AppWrap(
MotionWrap(MySection, 'app__mysection'),
'mysection',
'app__whitebg',
);In Navbar and NavigationDots, add the same id to the list (e.g. 'mysection'). In the container, use AppWrap(Component, 'mysection', ...) so the section has id="mysection".
portfolio, React, Vite, single-page application, mock data, Framer Motion, SASS, HOC, section navigation, contact form, testimonials, skills, works filter, frontend, developer portfolio, open source, headless CMS, Sanity.
This project is a React + Vite portfolio template with mock data, Framer Motion, and SASS. It shows how to structure a single-page app with section-based navigation, HOCs for layout and animation, and a simple data client that can be swapped later for a real API. Use it to learn React patterns, to build your own portfolio, or to reuse components and wrappers in other projects.
This project is licensed under the MIT License. Feel free to use, modify, and distribute the code as per the terms of the license.
This is an open-source project - feel free to use, enhance, and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://www.arnobmahmud.com.
Enjoy building and learning! π
Thank you! π





