A personal portfolio website with a Node.js/Express backend.
my-website/
├── server.js ← Express backend (handles contact form emails)
├── package.json ← project config and dependencies list
├── .env ← YOUR SECRET CONFIG (you create this, never commit it)
├── .env.example ← template showing what goes in .env
├── .gitignore ← tells Git what NOT to upload
└── public/ ← everything in here is served to visitors
├── index.html ← the main page (edit this for content)
├── style.css ← all visual styling (edit this for design)
├── script.js ← frontend JavaScript
└── images/ ← put your photos here
Download from https://nodejs.org — get the LTS version.
Open a terminal in this folder and run:
npm installThis reads package.json and downloads Express, Nodemailer, etc. into node_modules/.
Copy the example file:
cp .env.example .envThen open .env and fill in your real email credentials.
See .env.example for detailed instructions on getting a Gmail App Password.
npm run devOpen http://localhost:3000 in your browser. The server auto-restarts when you save changes (powered by nodemon).
→ Edit public/index.html
→ Search for ← change this comments throughout the file
→ Edit the :root block at the top of public/style.css
→ --accent controls the gold color throughout the entire site
→ In public/index.html, find the <iframe> in the #work section
→ Replace the src="..." value with your YouTube embed URL
→ Get this from YouTube → Share → Embed → copy the src value
- Put your image files in
public/images/ - In
public/index.html, find the#gallerysection - Replace each
<div class="gallery-placeholder">with:<img src="images/yourphoto.jpg" alt="Describe the photo" />
- Put your photo in
public/images/ - In
public/index.html, find<div class="about-photo"> - Replace the
.photo-placeholderdiv with:<img src="images/your-photo.jpg" alt="Your Name" />
→ In public/index.html, find the <div class="social-links"> in #contact
→ Change the href values to your real profile URLs
→ Find icons at https://fontawesome.com/icons
- Put your PDF in
public/files/resume.pdf - The download button in the About section will work automatically
- Create
public/about.html(or any name) - It's automatically available at http://localhost:3000/about.html
- To add a new route explicitly, in
server.js:app.get('/about', (req, res) => { res.sendFile(path.join(__dirname, 'public', 'about.html')); });
- Push this folder to GitHub
- Go to render.com, create a new Web Service
- Connect your GitHub repo
- Set:
- Build Command:
npm install - Start Command:
npm start
- Build Command:
- Add your .env variables in Render's "Environment" tab
- Deploy — it gives you a live URL
Similar to Render. Go to railway.app, connect GitHub repo, add env vars.
If you ever want to remove the backend entirely (use Formspree for contact),
you can drag the public/ folder to netlify.com/drop and it's live instantly.
"Cannot find module" error when running npm run dev
→ Run npm install first
Contact form says "Could not reach the server"
→ Make sure npm run dev is running
→ Check your .env file has valid email credentials
Emails not arriving after filling out the form → Check your .env EMAIL_USER and EMAIL_PASS → Make sure you're using a Gmail App Password (not your regular password) → Check your spam folder
Changes to HTML/CSS not showing → Hard refresh the browser: Ctrl+Shift+R (Windows) or Cmd+Shift+R (Mac)