Build static portfolio sites using Next.js 16 App Router, Redux Toolkit for content management, and Tailwind CSS with mobile-first responsive design.
Build modern static portfolio websites using Next.js 16 with App Router, Redux Toolkit for state-driven content, TypeScript for type safety, and Tailwind CSS for mobile-first responsive styling.
**CRITICAL: ALL static text and images MUST be driven by Redux state, never hardcoded in components.**
- Define its state interface
- Use `createSlice` from Redux Toolkit
- Export the slice reducer and action creators
- Keep initial state within the slice file
**Example Redux Slice:**
```typescript
import { createSlice } from '@reduxjs/toolkit';
interface ExperienceItem {
company: string;
role: string;
logo: string;
period: string;
}
interface ExperienceState {
title: string;
items: ExperienceItem[];
}
const initialState: ExperienceState = {
title: 'Experience',
items: [
{
company: 'Company Name',
role: 'Job Title',
logo: '/images/company-logo.png',
period: '2020 - Present'
}
]
};
export const experienceSlice = createSlice({
name: 'experience',
initialState,
reducers: {}
});
export default experienceSlice.reducer;
export type { ExperienceState, ExperienceItem };
```
**Example Section Component:**
```typescript
'use client';
import { useAppSelector } from '@/lib/hooks';
import Image from 'next/image';
export default function Experience() {
const experience = useAppSelector((state) => state.experience);
return (
<section id="experience" className="border-t-2 border-zinc-300 bg-white dark:border-zinc-700 dark:bg-black">
<div className="mx-auto max-w-6xl px-6 py-20">
<h2 className="mb-8 text-3xl font-bold">{experience.title}</h2>
<div className="space-y-8">
{experience.items.map((item, index) => (
<div key={index} className="flex flex-col items-center gap-4 sm:flex-row sm:items-start">
<div className="flex-shrink-0 sm:mr-4">
<Image
src={item.logo}
alt={`${item.company} logo`}
width={72}
height={72}
className="h-full w-full rounded-lg object-contain"
/>
</div>
<div className="flex-1 text-center sm:text-left">
<h3 className="font-semibold">{item.role}</h3>
<p className="text-zinc-600 dark:text-zinc-400">{item.company}</p>
<p className="text-sm text-zinc-500">{item.period}</p>
</div>
</div>
))}
</div>
</div>
</section>
);
}
```
**Mobile-First Responsive Design:**
**Standard Section Styling:**
**Responsive Layout Pattern:**
```tsx
<div className="flex flex-col items-center gap-4 sm:flex-row sm:items-start">
<div className="flex-shrink-0 sm:mr-4">{/* Logo/Image */}</div>
<div className="flex-1 text-center sm:text-left">{/* Content */}</div>
</div>
```
**Common Tailwind Utilities:**
**Example:**
```tsx
import Image from 'next/image';
<Image
src="/images/company-logo.png"
alt="Company Name logo"
width={72}
height={72}
className="h-full w-full rounded-lg object-contain"
/>
```
```
app/ # Next.js app directory
├── layout.tsx # Root layout with Redux provider
├── page.tsx # Home page
└── globals.css # Global styles
components/ # React components
lib/ # Utilities and Redux setup
├── store.ts # Redux store configuration
├── hooks.ts # Typed Redux hooks
└── slices/ # Redux slices
public/ # Static assets
└── images/ # Image files
```
**next.config.ts:**
```typescript
import type { NextConfig } from 'next';
const nextConfig: NextConfig = {
output: 'export',
images: {
unoptimized: true
}
};
export default nextConfig;
```
**Run linting before commits:**
```bash
npm run lint
```
**Key ESLint Rules to Follow:**
React/Next.js:
TypeScript:
General:
Accessibility:
**Step-by-step process:**
1. **Create or update Redux slice** in `lib/slices/`:
- Define state interface
- Set initial state with content
- Create slice with `createSlice`
- Export reducer and types
2. **Add slice to store** in `lib/store.ts`:
```typescript
import newSectionReducer from './slices/newSectionSlice';
export const store = configureStore({
reducer: {
// ... existing slices
newSection: newSectionReducer,
},
});
```
3. **Create component** in `components/`:
- Use `'use client'` if needed
- Import `useAppSelector` hook
- Select data from Redux state
- Implement responsive layout with Tailwind
4. **Add component to page** in `app/page.tsx`:
```typescript
import NewSection from '@/components/NewSection';
export default function Home() {
return (
<>
{/* ... existing sections */}
<NewSection />
</>
);
}
```
5. **Add images** to `public/images/` if needed
6. **Test responsive design** on mobile and desktop
7. **Run ESLint** and fix any warnings/errors
**Redux Hook Usage:**
```typescript
import { useAppSelector } from '@/lib/hooks';
const data = useAppSelector((state) => state.sliceName);
```
**Typed Redux Hooks (lib/hooks.ts):**
```typescript
import { useDispatch, useSelector } from 'react-redux';
import type { RootState, AppDispatch } from './store';
export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
export const useAppSelector = useSelector.withTypes<RootState>();
```
**Section with List Items:**
```tsx
<div className="space-y-8">
{items.map((item, index) => (
<div key={index} className="flex flex-col items-center gap-4 sm:flex-row sm:items-start">
{/* Item content */}
</div>
))}
</div>
```
**Build command:**
```bash
npm run build
```
**Configuration:**
1. **Plan content structure** - identify sections needed
2. **Create Redux slices** - define data structures and initial content
3. **Build components** - implement UI using Tailwind, consuming Redux state
4. **Add images** - place in `public/images/` and reference in slices
5. **Test responsiveness** - verify mobile, tablet, and desktop layouts
6. **Test dark mode** - ensure dark mode variants work correctly
7. **Run linter** - fix all ESLint errors and warnings
8. **Build for production** - `npm run build`
9. **Deploy** - upload `out/` directory to static hosting
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/nextjs-portfolio-with-redux-and-ssg/raw