I have been looking for a simple way to use typescript in my html projects for some time now. There is Webpack, but it needs a lot of configuration for even the simplest of things. There is always the option to use React or Angular, but that is too much for something quick. Then there is Parcel.js
Parcel.js is a fast, zero-config web application bundler that makes setting up a development environment a breeze. It comes with built-in support for TypeScript, which can be integrated into HTML with minimal hassle. Whether you’re building a simple project or something more complex, Parcel makes it easy to get up and running. In this article, we’ll explore how to use Parcel.js to set up a project with TypeScript, structure your project for scalability, and prepare it for deployment.
What You’ll Learn
- How to set up a Parcel.js project
- How to integrate TypeScript into your HTML
- The ideal project layout structure for Parcel.js projects
- How to build and deploy your project with Parcel.js
Setting Up Your Project
To get started with Parcel.js and TypeScript, you’ll first need to create a new project folder. Then, initialize your project with npm
or yarn
.
-
Initialize the Project
First, navigate to your project folder in your terminal and run:
mkdir parcel-typescript-demo cd parcel-typescript-demo npm init -y
This will create a
package.json
file with default values. -
Install Parcel and TypeScript
Next, install Parcel and TypeScript as development dependencies:
npm install --save-dev parcel typescript
-
Add TypeScript Configuration
Create a
tsconfig.json
file to configure TypeScript. This will tell TypeScript how to handle your code.{ "compilerOptions": { "target": "es6", "module": "esnext", "strict": true, "jsx": "react" }, "include": ["src/**/*"] }
-
Create Your HTML and TypeScript Files
Now you’re ready to create your files. In the root directory, create the following structure:
/parcel-typescript-demo ├── src │ ├── index.html │ └── app.ts ├── package.json ├── tsconfig.json
Adding TypeScript to HTML
In index.html
, you can add a <script>
tag to link to your TypeScript file. Parcel handles the compilation for you, so you don’t need to manually transpile TypeScript before using it in HTML.
Example: index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Parcel.js TypeScript Demo</title>
</head>
<body>
<h1>Welcome to Parcel.js with TypeScript!</h1>
<!-- Link to the TypeScript file -->
<script src="./app.ts"></script>
</body>
</html>
Example: app.ts
In app.ts
, you can start writing TypeScript code. Parcel will automatically bundle it when you run the development server.
const message: string = "Hello, Parcel with TypeScript!";
console.log(message);
const heading = document.querySelector("h1");
if (heading) {
heading.textContent = message;
}
Here, you declare a variable message
of type string
, and then modify the content of the <h1>
tag in your HTML based on that message.
Dev Server
You can serve the project by running parcel on index.html
parcel index.html
And that’s it!
Structuring Your Project
As your project grows, it’s important to have a well-organized structure. Here’s a basic layout that you can use for most projects:
/parcel-typescript-demo
├── src/
│ ├── assets/ # Static files like images and styles
│ ├── components/ # Reusable UI components (if any)
│ ├── index.html # Main HTML file
│ └── app.ts # Main TypeScript file
├── dist/ # Compiled output (generated by Parcel)
├── node_modules/ # Node.js modules
├── package.json # Project configuration
├── tsconfig.json # TypeScript configuration
Key Directories:
- src: This is where your main code lives. Keep all your TypeScript files, HTML files, and static assets (like CSS or images) here.
- dist: The
dist/
folder will contain the bundled files after running the build command. This is where your production-ready files will be placed. - node_modules: This folder contains all the packages installed via npm or yarn.
Building for Deployment
Once your project is ready, it’s time to build it for production. Parcel makes this process simple with just one command:
-
Build Your Project
To build your project for production, run:
npx parcel build src/index.html
This will create a
dist
folder with all your bundled and minified files, ready to be deployed. -
Deploy Your Project
The contents of the
dist
folder are all that you need to deploy your application. You can upload them to your favorite hosting provider, such as Netlify, Vercel, or GitHub Pages.For example, if you’re using GitHub Pages, you can push the
dist/
folder to your GitHub repository and configure the pages to serve from thedist
folder.
Examples
If you are just starting out with Typescript the following examples might come in handy.
Example 1: TypeScript Classes and DOM Manipulation
In this example, we’ll create a simple Button
class in TypeScript that interacts with the DOM. This will show how you can write more structured TypeScript code and integrate it with HTML.
Example: Button.ts
Create a new file called Button.ts
under src/
:
export class Button {
private element: HTMLButtonElement;
constructor(label: string) {
this.element = document.createElement("button");
this.element.textContent = label;
document.body.appendChild(this.element);
}
addClickListener(callback: () => void): void {
this.element.addEventListener("click", callback);
}
}
Example: app.ts
Now, modify app.ts
to use the Button
class:
import { Button } from "./Button";
const greetButton = new Button("Click me to greet");
greetButton.addClickListener(() => {
alert("Hello from Parcel + TypeScript!");
});
Explanation:
- Button.ts defines a simple
Button
class with methods for adding a label and listening for click events. - app.ts creates an instance of
Button
and listens for clicks, displaying an alert when clicked.
This example shows how to encapsulate code using TypeScript classes and interact with the DOM in a structured way.
Example 2: Using TypeScript Interfaces for Type Checking
TypeScript’s interface feature allows you to define the shape of an object. This is especially useful when you need to ensure that objects have the correct properties.
Example: User.ts
Create a file called User.ts
:
export interface User {
name: string;
age: number;
}
export function greetUser(user: User): string {
return `Hello, ${user.name}! You are ${user.age} years old.`;
}
Example: app.ts
Modify app.ts
to use the User
interface:
import { User, greetUser } from "./User";
const user: User = {
name: "Aliya",
age: 25,
};
console.log(greetUser(user));
Explanation:
- User.ts defines an interface
User
that specifies the structure of a user object. ThegreetUser
function uses this interface for type-checking. - app.ts creates an object that adheres to the
User
interface and uses thegreetUser
function to print a greeting.
This shows how to use TypeScript’s type-checking system with interfaces to define structured data, helping to catch potential errors early.
Example 3: Handling Events with TypeScript and the DOM
TypeScript allows you to type-check events, making it easier to work with event listeners and handlers. In this example, we’ll create an event listener for a form submission.
Example: Form.ts
Create a new file called Form.ts
:
export class Form {
private form: HTMLFormElement;
private input: HTMLInputElement;
constructor(formId: string) {
this.form = document.getElementById(formId) as HTMLFormElement;
this.input = this.form.querySelector("input") as HTMLInputElement;
}
addSubmitListener(callback: (inputValue: string) => void): void {
this.form.addEventListener("submit", (event: Event) => {
event.preventDefault();
callback(this.input.value);
});
}
}
Example: app.ts
Now, modify app.ts
to use the Form
class:
import { Form } from "./Form";
const form = new Form("myForm");
form.addSubmitListener((inputValue) => {
alert(`You submitted: ${inputValue}`);
});
Example: index.html
Make sure to include the form in your index.html
:
<form id="myForm">
<input type="text" placeholder="Enter something" />
<button type="submit">Submit</button>
</form>
Explanation:
- Form.ts defines a
Form
class that listens for a form submission and uses type checking to ensure the correct handling of form elements. - app.ts creates a form listener that triggers an alert when the form is submitted, showing the entered input.
This example demonstrates how to work with form events in TypeScript, adding type safety and structure.
Example 4: Using TypeScript with External APIs (Fetch)
In this example, we will fetch data from an external API using TypeScript and handle the response with types.
Example: Api.ts
Create a file called Api.ts
:
interface Post {
userId: number;
id: number;
title: string;
body: string;
}
export async function fetchPosts(): Promise<Post[]> {
const response = await fetch("https://jsonplaceholder.typicode.com/posts");
const posts: Post[] = await response.json();
return posts;
}
Example: app.ts
Modify app.ts
to fetch and display the posts:
import { fetchPosts } from "./Api";
async function displayPosts() {
const posts = await fetchPosts();
const postsList = document.createElement("ul");
posts.forEach((post) => {
const listItem = document.createElement("li");
listItem.textContent = `${post.title} - ${post.body}`;
postsList.appendChild(listItem);
});
document.body.appendChild(postsList);
}
displayPosts();
Explanation:
- Api.ts defines a function
fetchPosts
that fetches posts from a public API and returns the response typed as an array ofPost
objects. - app.ts calls
fetchPosts
to retrieve data and displays it on the web page.
This example shows how TypeScript helps ensure that your API responses are correctly typed, preventing common errors when working with external data.
Example 5: Using TypeScript with Modules
Parcel supports ES modules out of the box. In this example, we’ll split our code into multiple modules, demonstrating how to import and export functions or classes.
Example: mathUtils.ts
Create a file called mathUtils.ts
:
export function add(a: number, b: number): number {
return a + b;
}
export function subtract(a: number, b: number): number {
return a - b;
}
Example: app.ts
Now, modify app.ts
to use the functions from mathUtils.ts
:
import { add, subtract } from "./mathUtils";
const sum = add(5, 3);
const difference = subtract(10, 4);
console.log(`Sum: ${sum}, Difference: ${difference}`);
Explanation:
- mathUtils.ts defines two functions:
add
andsubtract
, which are exported to be used in other modules. - app.ts imports those functions and uses them to perform simple arithmetic operations.
This shows how to structure your TypeScript code into reusable modules.
Parcel.js simplifies web development by providing automatic handling of TypeScript integration, which allows you to focus more on writing code rather than managing configurations. In this tutorial, we covered:
- How to set up a Parcel.js project with TypeScript.
- How to integrate TypeScript into your HTML files.
- Structuring your project for maintainability and scalability.
- Building and deploying your project with a few simple commands.
PS: If you’re just starting out with TypeScript, consider enabling strict mode in your tsconfig.json
. It forces you to handle edge cases and can prevent bugs before they become problems!