TypeScript Tutorial: Creating a Simple Web-Based Portfolio

In today’s digital age, a personal website or portfolio is essential for showcasing your skills, projects, and experience to potential employers, clients, or collaborators. Building a website from scratch can seem daunting, but with TypeScript, we can create a robust and maintainable web-based portfolio that’s easy to update and customize. This tutorial will guide you through building a simple yet effective portfolio using TypeScript, focusing on clear explanations, practical examples, and step-by-step instructions. We’ll cover everything from setting up your project to deploying it online, ensuring you have a solid understanding of the concepts involved.

Why TypeScript?

TypeScript is a superset of JavaScript that adds static typing. This means you can define the types of variables, function parameters, and return values. Why is this important? Because it helps you catch errors early in the development process. TypeScript’s type checking can identify potential bugs before you even run your code, saving you time and frustration. It also improves code readability and maintainability. When you know the types of your variables, it’s easier to understand what the code is doing and how different parts of your application interact. Furthermore, TypeScript provides excellent tooling support, including autocompletion, refactoring, and code navigation, making your development workflow more efficient.

Prerequisites

Before we begin, make sure you have the following installed:

  • Node.js and npm (Node Package Manager): You can download them from https://nodejs.org/.
  • A code editor: Visual Studio Code is a popular choice and has excellent TypeScript support.

Project Setup

Let’s start by setting up our project. Open your terminal and create a new directory for your portfolio:

mkdir my-portfolio
cd my-portfolio

Initialize a new npm project:

npm init -y

This command creates a package.json file, which will manage our project dependencies. Next, install TypeScript and a few other packages we’ll need:

npm install typescript --save-dev
npm install parcel-bundler --save-dev

We’re using Parcel as a bundler to simplify the build process. It automatically handles things like bundling our JavaScript and CSS, and it also supports hot module replacement (HMR), which can significantly speed up your development workflow.

Now, let’s create a tsconfig.json file. This file configures the TypeScript compiler. Run the following command in your terminal:

npx tsc --init

This command generates a default tsconfig.json file. You can customize this file to suit your project’s needs. For our portfolio, we can use the following configuration:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"]
}
  • target: Specifies the JavaScript version to compile to (es5 for broader browser compatibility).
  • module: Specifies the module system (commonjs for Node.js compatibility).
  • outDir: Specifies the output directory for compiled JavaScript files.
  • strict: Enables strict type checking.
  • esModuleInterop: Enables interoperability between CommonJS and ES modules.
  • skipLibCheck: Skips type checking of declaration files.
  • forceConsistentCasingInFileNames: Enforces consistent casing in file names.
  • include: Specifies the files to be included in the compilation.

Create a directory named src in your project root, and inside it, create an index.html, index.ts and style.css file. Your project structure should now look like this:


my-portfolio/
├── package.json
├── tsconfig.json
├── src/
│   ├── index.html
│   ├── index.ts
│   └── style.css

HTML Structure (index.html)

Let’s start by setting up the basic HTML structure for our portfolio. Open src/index.html and add the following code:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div id="app"></div>
    <script src="index.ts"></script>
</body>
</html>

This HTML sets up the basic structure of the page, including a link to the CSS file and a script tag for our TypeScript file. The <div id="app"></div> element will be where we render our portfolio content.

CSS Styling (style.css)

Now, let’s add some basic styling to our style.css file. This is where you can customize the look and feel of your portfolio. For example:

body {
    font-family: sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f0f0f0;
    color: #333;
}

#app {
    max-width: 960px;
    margin: 20px auto;
    padding: 20px;
    background-color: #fff;
    border-radius: 8px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1, h2, h3 {
    color: #007bff;
}

p {
    line-height: 1.6;
}

/* Add more styles as needed */

Feel free to customize this CSS to match your design preferences. You can add styles for headings, paragraphs, links, images, and other elements.

TypeScript Implementation (index.ts)

This is where the magic happens! We’ll write TypeScript code to dynamically generate the content of our portfolio. Open src/index.ts and add the following code:


// Define an interface for a project
interface Project {
  title: string;
  description: string;
  imageUrl: string;
  link: string;
}

// Sample projects data
const projects: Project[] = [
  {
    title: "Project 1",
    description: "A brief description of Project 1.",
    imageUrl: "https://via.placeholder.com/300", // Replace with actual image URL
    link: "#", // Replace with project link
  },
  {
    title: "Project 2",
    description: "A brief description of Project 2.",
    imageUrl: "https://via.placeholder.com/300", // Replace with actual image URL
    link: "#", // Replace with project link
  },
  // Add more projects as needed
];

// Function to create a project card
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;
  image.style.width = '100%'; // Ensure images fit the container
  image.style.marginBottom = '10px';

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

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

  const link = document.createElement('a');
  link.href = project.link;
  link.textContent = 'View Project';
  link.target = '_blank'; // Open link in a new tab

  card.appendChild(image);
  card.appendChild(title);
  card.appendChild(description);
  card.appendChild(link);

  return card;
}

// Function to render the portfolio
function renderPortfolio(): void {
  const app = document.getElementById('app');

  if (!app) {
    console.error('App element not found!');
    return;
  }

  // Create a heading
  const heading = document.createElement('h1');
  heading.textContent = 'My Portfolio';
  app.appendChild(heading);

  // Create a projects container
  const projectsContainer = document.createElement('div');
  projectsContainer.classList.add('projects-container');

  // Render each project
  projects.forEach(project => {
    const card = createProjectCard(project);
    projectsContainer.appendChild(card);
  });

  app.appendChild(projectsContainer);
}

// Call the render function when the page loads
document.addEventListener('DOMContentLoaded', renderPortfolio);

Let’s break down this code:

  • Interfaces: We define an Project interface to ensure that our project data has a consistent structure, including a title, description, image URL, and link. This improves code readability and helps prevent errors.
  • Sample Data: We create an array of projects, which contains sample project data. You’ll replace this with your actual project details.
  • createProjectCard Function: This function takes a Project object and returns an HTML div element representing a project card. It dynamically creates HTML elements for the image, title, description, and link, and then styles them.
  • renderPortfolio Function: This function finds the #app element in the HTML, creates a heading, and then iterates through the projects array. For each project, it calls the createProjectCard function to generate a project card and appends it to the #app element.
  • Event Listener: We use document.addEventListener('DOMContentLoaded', renderPortfolio) to ensure that the renderPortfolio function is called after the HTML document has been fully loaded.

Adding CSS for project cards

To style the project cards, add the following CSS to your style.css file:


.projects-container {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); /* Responsive grid */
  gap: 20px;
}

.project-card {
  border: 1px solid #ddd;
  padding: 15px;
  border-radius: 8px;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}

.project-card img {
  max-width: 100%;
  height: auto;
  border-radius: 4px;
}

.project-card h3 {
  margin-top: 0;
  margin-bottom: 5px;
}

.project-card p {
  margin-bottom: 10px;
}

.project-card a {
  display: inline-block;
  padding: 8px 15px;
  background-color: #007bff;
  color: #fff;
  text-decoration: none;
  border-radius: 4px;
}

.project-card a:hover {
  background-color: #0056b3;
}

This CSS creates a responsive grid layout for the project cards, adds borders and padding, styles the images and headings, and styles the links.

Building and Running the Application

Now that we have our code written, let’s build and run our portfolio. Open your terminal and run the following command:

npx parcel src/index.html

Parcel will bundle your code and start a development server. You should see a message in your terminal indicating that the server is running and the port it is listening on (usually 1234). Open your web browser and go to http://localhost:1234. You should see your portfolio with the sample project cards displayed.

Adding More Content

To make your portfolio more useful, you can add more content, such as:

  • About Me Section: Add a section that tells visitors about yourself, your skills, and your experience.
  • Skills Section: List your technical skills, such as programming languages, frameworks, and tools you’re proficient in.
  • Contact Form: Include a contact form so visitors can easily reach you.
  • Blog/Articles Section: Link to your blog or articles to showcase your writing skills.
  • Resume Download: Provide a link to download your resume.
  • Animations and Interactivity: Use CSS transitions or JavaScript to add animations and make your portfolio more engaging.

Here’s an example of how you might add an “About Me” section in your index.ts file:


// ... (Existing code)

interface AboutMe {
  title: string;
  content: string;
}

const aboutMeData: AboutMe = {
  title: 'About Me',
  content: "I am a passionate software engineer with experience in..."
};

function createAboutMeSection(data: AboutMe): HTMLDivElement {
  const section = document.createElement('div');
  section.classList.add('about-me');

  const title = document.createElement('h2');
  title.textContent = data.title;

  const content = document.createElement('p');
  content.textContent = data.content;

  section.appendChild(title);
  section.appendChild(content);

  return section;
}

function renderPortfolio(): void {
  const app = document.getElementById('app');

  if (!app) {
    console.error('App element not found!');
    return;
  }

  const heading = document.createElement('h1');
  heading.textContent = 'My Portfolio';
  app.appendChild(heading);

  const aboutMeSection = createAboutMeSection(aboutMeData);
  app.appendChild(aboutMeSection);

  const projectsContainer = document.createElement('div');
  projectsContainer.classList.add('projects-container');

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

  app.appendChild(projectsContainer);
}

And add the following CSS to your style.css:


.about-me {
  margin-bottom: 20px;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 8px;
}

.about-me h2 {
  margin-top: 0;
  margin-bottom: 10px;
}

Remember to replace the placeholder content with your actual information. Similarly, you can create sections for skills, contact information, and other relevant details. Each section should have its own data structure (interface) and a corresponding function to generate the HTML. This modular approach makes your code easier to manage and update.

Common Mistakes and How to Fix Them

Here are some common mistakes and how to avoid them:

  • Incorrect File Paths: Double-check your file paths in the HTML, CSS, and TypeScript files. Incorrect paths can lead to errors when the browser tries to load the files.
  • Typos: TypeScript can catch typos in your code. Make sure you are using the correct variable names, function names, and property names.
  • Missing Types: Always define the types for your variables, function parameters, and return values. This makes your code more readable, helps prevent errors, and allows TypeScript to catch type-related issues.
  • Incorrect CSS Selectors: Ensure your CSS selectors match the HTML elements you are trying to style. Use your browser’s developer tools to inspect the elements and check if the CSS styles are being applied.
  • Not Using the Development Server: Use the development server (Parcel in this case) to automatically reload your changes. Otherwise, you’ll have to refresh the browser manually every time you make changes.

SEO Best Practices

To make your portfolio more visible in search results, follow these SEO best practices:

  • Title Tag: Use a descriptive title tag in your HTML <head> section (e.g., “Your Name – Software Engineer Portfolio”).
  • Meta Description: Write a concise meta description that accurately summarizes your portfolio (e.g., “John Doe’s portfolio showcasing software engineering projects and skills.”).
  • Heading Tags: Use heading tags (<h1>, <h2>, <h3>, etc.) to structure your content and make it more readable.
  • Keyword Optimization: Naturally incorporate relevant keywords throughout your content (e.g., “software engineer,” “TypeScript,” “portfolio,” “web development”).
  • Image Optimization: Use descriptive alt text for your images to help search engines understand their content. Optimize image file sizes to improve page load speed.
  • Mobile Responsiveness: Ensure your portfolio is responsive and looks good on all devices.
  • Sitemap: Create a sitemap and submit it to search engines to help them crawl your website.
  • Website speed: Optimize the speed of your website to improve user experience and search engine rankings. Consider image optimization, code minification, and caching.

Key Takeaways

  • TypeScript helps you write more robust and maintainable code by adding static typing.
  • Interfaces provide a way to define the structure of your data.
  • Parcel is a convenient bundler that simplifies the build process.
  • Dynamic content generation can be achieved using JavaScript to manipulate the DOM.
  • A well-structured portfolio is essential for showcasing your skills and experience.

FAQ

  1. Can I use a different bundler instead of Parcel? Yes, you can. Popular alternatives include Webpack and Rollup. However, Parcel is easier to set up for beginners.
  2. How do I deploy my portfolio online? You can deploy your portfolio to services like Netlify, Vercel, or GitHub Pages. These services provide free hosting and make it easy to deploy static websites.
  3. How do I add more projects to my portfolio? Simply add more objects to the projects array in your index.ts file and update the image URLs and project links.
  4. Can I use a CSS framework like Bootstrap or Tailwind CSS? Yes, you can. These frameworks provide pre-built CSS components that can speed up your development. Install them using npm and import them into your CSS file.
  5. How do I handle user input (e.g., contact form)? For a contact form, you’ll need a backend to handle form submissions. You can use services like Formspree or Netlify Forms to handle form submissions without setting up a backend server.

Building a web-based portfolio with TypeScript is a fantastic way to showcase your skills and projects. By following the steps outlined in this tutorial, you can create a professional-looking portfolio that’s easy to customize and update. Remember to replace the sample data with your own information and experiment with different styling and content to make your portfolio unique. As you gain more experience, you can add more advanced features, such as animations, interactive elements, and a backend to handle user input. The world of web development is constantly evolving, so keep learning and experimenting to stay ahead of the curve. With each project, you’ll hone your skills and become a more proficient TypeScript developer. Building a portfolio is not just about showing off your work; it’s about demonstrating your commitment to continuous learning and your ability to adapt to new technologies. It’s a living document that evolves with you, reflecting your growth and achievements.