TypeScript Tutorial: Building a Simple Interactive Portfolio

In today’s digital landscape, a well-crafted online portfolio is crucial for showcasing your skills, projects, and experiences. Whether you’re a developer, designer, writer, or any creative professional, a portfolio serves as your digital resume, allowing potential clients and employers to see your work firsthand. Building a portfolio from scratch can seem daunting, but with TypeScript, we can create a dynamic and interactive portfolio that is easy to update and visually appealing. This tutorial will guide you through the process of building a simple, yet effective, portfolio website using TypeScript, HTML, and CSS. We’ll focus on creating a user-friendly interface with features like project previews, skill displays, and contact information, all while leveraging the benefits of TypeScript’s type safety and code organization.

Why TypeScript for Your Portfolio?

TypeScript, a superset of JavaScript, brings several advantages to web development, especially when building projects like a portfolio. Let’s explore why TypeScript is an excellent choice:

  • Type Safety: TypeScript adds static typing to JavaScript. This means you can define the types of variables, function parameters, and return values. This helps catch errors during development, preventing unexpected behavior and making your code more reliable.
  • Code Organization: TypeScript promotes better code organization through features like interfaces, classes, and modules. This makes your code more maintainable and scalable as your portfolio grows.
  • Improved Developer Experience: TypeScript provides better autocompletion, refactoring, and error checking in your code editor, leading to a more productive development experience.
  • Modern JavaScript Features: TypeScript supports the latest JavaScript features, allowing you to write cleaner and more efficient code.

Setting Up Your Development Environment

Before we start coding, let’s set up our development environment. You’ll need the following:

  • Node.js and npm (Node Package Manager): These are essential for managing project dependencies and running TypeScript. You can download them from nodejs.org.
  • A Code Editor: Choose your preferred code editor (e.g., VS Code, Sublime Text, Atom).
  • TypeScript Compiler: Install the TypeScript compiler globally using npm: npm install -g typescript

Once you have these installed, create a new project directory for your portfolio. Navigate to the directory in your terminal and initialize a new npm project by running: npm init -y. This creates a package.json file, which will store your project’s dependencies.

Project Structure

Let’s define a basic project structure to keep our code organized:

portfolio-project/
├── src/
│   ├── index.ts          # Main TypeScript file
│   ├── components/
│   │   ├── ProjectCard.ts    # Component for project cards
│   │   └── SkillBar.ts      # Component for skill bars
│   ├── models/
│   │   ├── Project.ts       # Interface for project data
│   │   └── Skill.ts         # Interface for skill data
│   └── styles/
│       └── styles.css        # CSS file for styling
├── public/
│   ├── index.html        # HTML file
│   └── assets/
│       └── images/         # Images for projects and skills
├── tsconfig.json       # TypeScript configuration file
└── package.json

Create these files and folders in your project directory.

Configuring TypeScript

Create a tsconfig.json file in the root of your project. This file configures the TypeScript compiler. Here’s a basic configuration:

{
  "compilerOptions": {
    "target": "ES5",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}

Explanation of key options:

  • target: Specifies the JavaScript version to compile to (ES5 is widely supported).
  • module: Defines the module system (commonjs is common for Node.js projects).
  • outDir: The directory where compiled JavaScript files will be placed.
  • rootDir: The root directory of your TypeScript files.
  • strict: Enables strict type checking.
  • esModuleInterop: Enables interoperability between CommonJS and ES modules.
  • skipLibCheck: Skips type checking of declaration files (improves compile time).
  • forceConsistentCasingInFileNames: Enforces consistent casing in filenames.
  • include: Specifies which files to include in the compilation.

Creating the HTML Structure

Open public/index.html and add the basic HTML structure. This will serve as the foundation for your portfolio. Include links to your CSS and JavaScript files.

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Your Name's Portfolio</title>
  <link rel="stylesheet" href="styles.css">
</head>
<body>
  <header>
    <h1>Your Name</h1>
    <p>Your Title/Profession</p>
  </header>

  <main>
    <section id="projects">
      <h2>Projects</h2>
      <div id="project-container">
        <!-- Project cards will be added here -->
      </div>
    </section>

    <section id="skills">
      <h2>Skills</h2>
      <div id="skill-container">
        <!-- Skill bars will be added here -->
      </div>
    </section>

    <section id="contact">
      <h2>Contact</h2>
      <p>Contact information here.</p>
    </section>
  </main>

  <footer>
    <p>© <script>document.write(new Date().getFullYear());</script> Your Name. All rights reserved.</p>
  </footer>
  <script src="index.js"></script>
</body>
</html>

Defining Data Models

Let’s create interfaces to define the structure of our data. Open src/models/Project.ts and add the following:

export interface Project {
  title: string;
  description: string;
  imageUrl: string;
  liveLink?: string; // Optional link to the live project
  githubLink?: string; // Optional link to the GitHub repository
  tags: string[];
}

This interface defines the properties of a project, including title, description, image URL, and optional links. Now, open src/models/Skill.ts and add:

export interface Skill {
  name: string;
  level: number; // Percentage (0-100)
}

This interface defines the structure for a skill, including its name and proficiency level (as a percentage).

Creating Components

Components are reusable building blocks for your portfolio. Let’s create two components: ProjectCard and SkillBar.

ProjectCard Component

Open src/components/ProjectCard.ts and add the following code:

import { Project } from '../models/Project';

export function createProjectCard(project: Project): HTMLDivElement {
  const card = document.createElement('div');
  card.classList.add('project-card');

  const image = document.createElement('img');
  image.src = project.imageUrl;
  image.alt = project.title;
  card.appendChild(image);

  const title = document.createElement('h3');
  title.textContent = project.title;
  card.appendChild(title);

  const description = document.createElement('p');
  description.textContent = project.description;
  card.appendChild(description);

  if (project.liveLink) {
    const liveLink = document.createElement('a');
    liveLink.href = project.liveLink;
    liveLink.textContent = 'Live Demo';
    liveLink.target = '_blank';
    card.appendChild(liveLink);
  }

  if (project.githubLink) {
    const githubLink = document.createElement('a');
    githubLink.href = project.githubLink;
    githubLink.textContent = 'GitHub';
    githubLink.target = '_blank';
    card.appendChild(githubLink);
  }

  return card;
}

This function creates a div element for each project card, populating it with the project’s data. It also includes conditional links to the live demo and GitHub repository if they are provided.

SkillBar Component

Open src/components/SkillBar.ts and add the following code:

import { Skill } from '../models/Skill';

export function createSkillBar(skill: Skill): HTMLDivElement {
  const skillBarContainer = document.createElement('div');
  skillBarContainer.classList.add('skill-bar-container');

  const skillName = document.createElement('span');
  skillName.textContent = skill.name;
  skillBarContainer.appendChild(skillName);

  const skillBar = document.createElement('div');
  skillBar.classList.add('skill-bar');
  skillBar.style.width = `${skill.level}%`;
  skillBarContainer.appendChild(skillBar);

  return skillBarContainer;
}

This function creates a skill bar with a label and a filled-in bar representing the skill level.

Implementing the Main Logic (index.ts)

Now, let’s write the main logic in src/index.ts. This file will fetch your project and skill data, create the components, and render them in the HTML.

import { createProjectCard } from './components/ProjectCard';
import { createSkillBar } from './components/SkillBar';
import { Project } from './models/Project';
import { Skill } from './models/Skill';

// Sample data (replace with your own data)
const projects: Project[] = [
  {
    title: 'Project 1',
    description: 'A brief description of Project 1.',
    imageUrl: 'assets/images/project1.jpg',
    liveLink: 'https://example.com/project1',
    githubLink: 'https://github.com/yourusername/project1',
    tags: ['TypeScript', 'React', 'Web Design'],
  },
  {
    title: 'Project 2',
    description: 'A brief description of Project 2.',
    imageUrl: 'assets/images/project2.jpg',
    githubLink: 'https://github.com/yourusername/project2',
    tags: ['JavaScript', 'Node.js', 'API'],
  },
];

const skills: Skill[] = [
  { name: 'TypeScript', level: 85 },
  { name: 'JavaScript', level: 90 },
  { name: 'React', level: 75 },
  { name: 'HTML/CSS', level: 95 },
];

function renderProjects(): void {
  const projectContainer = document.getElementById('project-container');
  if (!projectContainer) return;

  projects.forEach((project) => {
    const card = createProjectCard(project);
    projectContainer.appendChild(card);
  });
}

function renderSkills(): void {
  const skillContainer = document.getElementById('skill-container');
  if (!skillContainer) return;

  skills.forEach((skill) => {
    const skillBar = createSkillBar(skill);
    skillContainer.appendChild(skillBar);
  });
}

function main(): void {
  renderProjects();
  renderSkills();
}

main();

In this file:

  • We import the necessary functions and interfaces.
  • We define sample project and skill data (replace this with your actual data).
  • renderProjects() iterates through the projects and calls createProjectCard() to generate the project cards, then appends them to the project-container element in the HTML.
  • renderSkills() iterates through the skills and calls createSkillBar() to generate the skill bars, then appends them to the skill-container element in the HTML.
  • The main() function calls both render functions.

Styling with CSS

Now, let’s add some CSS to style your portfolio. Open src/styles/styles.css and add the following:

/* General styles */
body {
  font-family: sans-serif;
  margin: 0;
  padding: 0;
  background-color: #f4f4f4;
  color: #333;
  line-height: 1.6;
}

header, footer {
  background-color: #333;
  color: #fff;
  padding: 1rem 0;
  text-align: center;
}

main {
  padding: 20px;
  max-width: 960px;
  margin: 0 auto;
}

section {
  margin-bottom: 20px;
  padding: 20px;
  background-color: #fff;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h2 {
  border-bottom: 2px solid #ccc;
  padding-bottom: 10px;
}

a {
  color: #007bff;
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* Project card styles */
.project-card {
  border: 1px solid #ccc;
  padding: 15px;
  margin-bottom: 15px;
  border-radius: 5px;
  background-color: #fff;
}

.project-card img {
  max-width: 100%;
  height: auto;
  margin-bottom: 10px;
}

/* Skill bar styles */
.skill-bar-container {
  margin-bottom: 10px;
}

.skill-bar {
  background-color: #ddd;
  height: 20px;
  border-radius: 5px;
  margin-top: 5px;
}

.skill-bar-container span {
  display: block;
  margin-bottom: 2px;
}

This CSS provides basic styling for the overall layout, project cards, and skill bars. You can customize this to match your desired aesthetic.

Compiling and Running Your Portfolio

To compile your TypeScript code, run the following command in your terminal:

tsc

This command will compile all your TypeScript files into JavaScript files in the dist directory. If you set up your project correctly, the compiled JavaScript should be in the `dist` directory. If you are using a bundler (like Webpack or Parcel), you’ll need to configure it to handle TypeScript files and output the bundled JavaScript. Since this tutorial focuses on a simple setup, we’ll manually include the generated JavaScript file in our HTML.

Now, open your public/index.html file in your browser. You should see your portfolio with project cards and skill bars. If you don’t see anything, check the browser’s developer console (usually accessed by pressing F12) for any errors. Double-check that the paths to your CSS and JavaScript files in index.html are correct, and that your `index.ts` file is being compiled correctly.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to fix them:

  • Incorrect File Paths: Ensure that the file paths in your HTML (for CSS and JavaScript) and in your TypeScript files (for imports) are correct. Incorrect paths are a frequent source of errors.
  • Typos: TypeScript helps prevent typos, but they can still occur. Carefully check variable names, function names, and property names.
  • Missing Imports: Make sure you import all the necessary modules and functions. TypeScript will usually give you an error if an import is missing.
  • Incorrect Data Types: If you get type errors, double-check the data types of variables and function parameters.
  • Compiler Errors: If you encounter compiler errors, read the error messages carefully. They often provide valuable clues about what’s wrong. For example, if you see an error like “Cannot find module ‘./models/Project’”, it means your import path is incorrect, or the file doesn’t exist.

Enhancements and Next Steps

This tutorial provides a basic foundation for your portfolio. Here are some ways to enhance it:

  • Add more projects and skills: Populate your portfolio with your actual projects and skills.
  • Implement a contact form: Add a contact form using HTML, CSS, and JavaScript (or a backend service) to allow visitors to contact you.
  • Use a CSS framework: Consider using a CSS framework like Bootstrap or Tailwind CSS to speed up styling.
  • Add animations and transitions: Enhance the user experience with animations and transitions.
  • Make it responsive: Ensure your portfolio looks good on all devices by using responsive design techniques.
  • Use a bundler (Webpack, Parcel): For more complex projects, consider using a module bundler to manage dependencies and optimize your code.
  • Deploy Your Portfolio: Deploy your portfolio to a hosting platform like Netlify, Vercel, or GitHub Pages.

Summary / Key Takeaways

In this tutorial, we’ve built a simple, interactive portfolio using TypeScript, HTML, and CSS. We’ve covered the benefits of TypeScript, set up a development environment, created data models and components, and implemented the main logic to render project cards and skill bars. By following this guide, you should now have a functional portfolio that you can customize to showcase your unique skills and projects. Remember to replace the sample data with your own information and continue to iterate on your portfolio to make it even more impressive.

Building a portfolio is an ongoing process. As you learn new skills and complete new projects, update your portfolio to reflect your growth. The more you put into your portfolio, the more you’ll get out of it. A well-maintained portfolio is a testament to your commitment to your craft and a powerful tool for attracting opportunities. Continue to experiment with different design elements, layouts, and interactive features to make your portfolio stand out. With each iteration, your portfolio will become a more accurate and compelling representation of your abilities and experience, helping you make a lasting impression on potential employers and clients. Keep learning, keep building, and keep showcasing your best work – your future self will thank you for it.