Build a Simple Travel Planner — travel itinerary planner javascript
Planning a trip should be fun, not chaotic. This beginner-friendly tutorial shows you how to build a lightweight travel itinerary planner JavaScript web app that helps you add destinations, manage travel dates, add notes, and export or import your plans. No external APIs — everything runs in the browser and stores data in localStorage. By the end you'll have a fully working project you can paste into a single HTML file and publish on your blog.
This article is written for beginners and also optimized for on-page SEO around the keyword travel itinerary planner javascript. Read on for the full source code, explanation, and step-by-step instructions.
Why build a travel itinerary planner in JavaScript?
- Learn practical DOM and localStorage skills — this project teaches DOM manipulation, events, date handling, and persistence without servers.
- Fast to implement — a single-page HTML/CSS/JS app that you can publish immediately.
- SEO-friendly tutorial idea — topics like "travel itinerary planner JavaScript", "travel planner localStorage", and "simple travel itinerary web app" are easy to rank for because they target beginners who search for hands-on projects.
Features of the Travel Planner
Core Features
localStorage so they remain after refresh.Bonus UX Features
Step-by-step plan
- HTML structure — a form to add/edit trips, a list to show saved trips, and controls for export/import and filter.
- CSS styling — simple, readable layout; mobile-first; focus on usability and performance for PageSpeed.
- JavaScript logic — data model, CRUD operations (create, read, update, delete), save/load to
localStorage, export/import, and UI rendering.
We'll walk through each part below and then present the full code.
Full Source Code (HTML, CSS, JS)
Copy and paste the entire block into a single .html file and open it in a browser. No build steps required.
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Travel Planner JavaScript Project</title>
<meta name="description" content="Travel itinerary planner JavaScript project to organize trips with location and date management. Add, edit, delete, and search your trips easily.">
<style>
*{box-sizing:border-box;}
body{font-family:'Segoe UI',sans-serif;background:#f8fafc;margin:0;padding:0;color:#111827;}
header{background:#fff;border-bottom:1px solid #e5e7eb;padding:15px 25px;position:sticky;top:0;z-index:1000;}
header h1{font-size:1.6rem;margin:0;color:#111827;}
main{display:flex;flex-wrap:wrap;gap:1.5rem;padding:25px;align-items:flex-start;}
section,aside{background:#fff;border:1px solid #e5e7eb;border-radius:15px;padding:20px;flex:1;min-width:280px;}
h2{font-size:1.2rem;color:#1f2937;border-bottom:2px solid #e5e7eb;padding-bottom:8px;margin-bottom:15px;}
input,textarea{width:100%;padding:8px 10px;border:1px solid #d1d5db;border-radius:8px;margin-bottom:10px;font-size:0.95rem;}
label{font-weight:500;}
button{border:none;cursor:pointer;padding:10px 16px;border-radius:50px;background:#2563eb;color:#fff;font-size:0.95rem;transition:background 0.2s;}
button:hover{background:#1d4ed8;}
button.ghost{background:#f3f4f6;color:#111827;}
button.ghost:hover{background:#e5e7eb;}
.trip-item{border-bottom:1px solid #e5e7eb;padding:8px 0;}
.trip-item:last-child{border-bottom:none;}
.trip-title{font-weight:600;}
.trip-dates{font-size:0.85rem;color:#6b7280;}
.controls{display:flex;gap:8px;margin-top:10px;}
.muted{color:#6b7280;}
.row{display:flex;gap:8px;}
.search-box{margin-bottom:15px;}
@media(max-width:768px){main{flex-direction:column;}}
</style>
</head>
<body>
<header><h1>Travel Planner</h1></header>
<main>
<!-- Left: Trip List -->
<section>
<h2>Your Planned Trips</h2>
<!-- 🔍 Search Box -->
<div class="search-box">
<input type="text" id="searchInput" placeholder="Search trips by destination..." />
</div>
<div id="tripList"></div>
</section>
<!-- Right: Add/Edit Form -->
<aside aria-labelledby="add-trip">
<h2 id="add-trip">Add / Edit Trip</h2>
<form id="tripForm">
<input type="hidden" id="tripId"/>
<div>
<label for="title">Destination / Title</label>
<input type="text" id="title" placeholder="e.g., Paris, France" required/>
</div>
<div class="row">
<div style="flex:1">
<label for="startDate">Start Date</label>
<input type="date" id="startDate" required/>
</div>
<div style="flex:1">
<label for="endDate">End Date</label>
<input type="date" id="endDate" required/>
</div>
</div>
<div>
<label for="notes">Notes</label>
<textarea id="notes" placeholder="Optional notes: hotel, flight number, reminders"></textarea>
</div>
<div class="controls">
<button type="submit" id="saveBtn">Add Trip</button>
<button type="button" id="resetBtn" class="ghost">Reset</button>
</div>
<p id="message" class="muted" style="margin-top:8px;font-size:0.9rem"></p>
</form>
<!-- Recently Added Trips -->
<div id="recentTrips" style="margin-top:20px;">
<h3 style="margin-bottom:8px;">Recently Added Trips</h3>
<ul id="recentList" style="list-style:none;padding:0;margin:0;color:#374151;font-size:0.95rem;">
<li class="muted">No trips added yet.</li>
</ul>
</div>
</aside>
</main>
<script>
const tripForm=document.getElementById('tripForm');
const tripList=document.getElementById('tripList');
const searchInput=document.getElementById('searchInput');
const message=document.getElementById('message');
let trips=JSON.parse(localStorage.getItem('trips')||'[]');
function escapeHtml(str){return str.replace(/[&<>"']/g,t=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[t]));}
function formatDate(d){if(!d)return'';return new Date(d).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'});}
function renderTrips(filterText=''){
tripList.innerHTML='';
const filtered=trips.filter(t=>t.title.toLowerCase().includes(filterText.toLowerCase()));
if(filtered.length===0){tripList.innerHTML='<p class="muted">No matching trips found.</p>';return;}
filtered.forEach(t=>{
const div=document.createElement('div');
div.className='trip-item';
div.innerHTML=`
<div class="trip-title">${escapeHtml(t.title)}</div>
<div class="trip-dates">${formatDate(t.startDate)} → ${formatDate(t.endDate)}</div>
<p class="muted">${escapeHtml(t.notes||'')}</p>
<div class="controls">
<button onclick="editTrip('${t.id}')">Edit</button>
<button class="ghost" onclick="deleteTrip('${t.id}')">Delete</button>
</div>`;
tripList.appendChild(div);
});
showRecentTrips();
}
tripForm.addEventListener('submit',e=>{
e.preventDefault();
const id=document.getElementById('tripId').value;
const title=document.getElementById('title').value.trim();
const startDate=document.getElementById('startDate').value;
const endDate=document.getElementById('endDate').value;
const notes=document.getElementById('notes').value.trim();
if(!title){message.textContent='Please enter destination.';return;}
if(id){
const i=trips.findIndex(t=>t.id===id);
if(i!==-1)trips[i]={...trips[i],title,startDate,endDate,notes};
message.textContent='Trip updated!';
}else{
trips.push({id:Date.now().toString(),title,startDate,endDate,notes,createdAt:new Date()});
message.textContent='Trip added!';
}
localStorage.setItem('trips',JSON.stringify(trips));
renderTrips(searchInput.value);
tripForm.reset();
document.getElementById('tripId').value='';
document.getElementById('saveBtn').textContent='Add Trip';
setTimeout(()=>message.textContent='',2000);
});
document.getElementById('resetBtn').addEventListener('click',()=>{
tripForm.reset();
document.getElementById('tripId').value='';
document.getElementById('saveBtn').textContent='Add Trip';
});
function editTrip(id){
const t=trips.find(x=>x.id===id);
if(!t)return;
document.getElementById('tripId').value=t.id;
document.getElementById('title').value=t.title;
document.getElementById('startDate').value=t.startDate;
document.getElementById('endDate').value=t.endDate;
document.getElementById('notes').value=t.notes;
document.getElementById('saveBtn').textContent='Update Trip';
}
function deleteTrip(id){
if(confirm('Delete this trip?')){
trips=trips.filter(t=>t.id!==id);
localStorage.setItem('trips',JSON.stringify(trips));
renderTrips(searchInput.value);
}
}
function showRecentTrips(){
const list=document.getElementById('recentList');
if(trips.length===0){list.innerHTML='<li class="muted">No trips added yet.</li>';return;}
const recent=[...trips].sort((a,b)=>new Date(b.createdAt)-new Date(a.createdAt)).slice(0,3);
list.innerHTML=recent.map(t=>`
<li style="padding:6px 0;border-bottom:1px solid #e5e7eb;">
<strong>${escapeHtml(t.title)}</strong><br>
<small class="muted">${formatDate(t.startDate)} → ${formatDate(t.endDate)}</small>
</li>`).join('');
}
searchInput.addEventListener('input',()=>renderTrips(searchInput.value));
renderTrips();
</script>
</body>
</html>How the Travel Planner works (plain English)
Data model and storage
- Each trip is stored as a JavaScript object
{ id, title, startDate, endDate, notes, createdAt }. - All trips are saved together as an array in the browser's
localStorageunder the keytravel_planner_trips_v1. This means your trips stay saved on the same device/browser — no server needed.
Adding and editing trips
- When you fill the form and submit, the script checks basic validation (title present, start date ≤ end date).
- If the hidden
tripIdinput has a value, the form updates an existing trip. If not, it creates a new trip with a generated ID.
Listing, filtering, and sorting
- Trips are rendered from the
tripsarray to the page by creating DOM nodes. - The search box filters by title and notes (case-insensitive).
- Date filters (
filterStart,filterEnd) limit results to trips that overlap the selected range. - The sort dropdown changes the order (
start date ascending,title A–Z, etc.).
Export and Import
- Export creates a
.jsonfile with all trips (downloaded locally). - Import reads a JSON file you choose and attempts to parse trips and merge them with existing ones. Imported trips must include
title,startDate, andendDate.
Deleting and duplicating
- Each trip shows action buttons: Edit, Duplicate, and Delete.
- Delete removes the trip from
tripsand updateslocalStorage. Duplicate copies a trip with a new ID.
Accessibility and usability
- Buttons and inputs use accessible labels and a clear layout to help all users.
- Messages inform users of success/failure (like "Trip added" or "Trip deleted").
Code comments & usage instructions
- The HTML contains three main parts: header, left column (trip list + controls), and right column (add/edit form).
- The CSS is small and intentionally inline in the
<head>so you can paste the whole file into one.htmlfile and run it. - The JavaScript is placed just before
</body>and contains:- Utility helpers (
qs,STORAGE_KEY,genId,formatDate). loadTrips()andsaveTrips()to read/writelocalStorage.renderTrips()that reads UI filters and displays the matching trips.- Event handlers for form submission, file import, export, and click actions on individual trip buttons.
- Utility helpers (
Usage instructions — quick start
- Save the code above as
travel-planner.html. - Open the file in any modern browser (Chrome, Firefox, Safari).
- Add a trip: fill destination, select start and end dates, optional notes — click Add Trip.
- Edit: click Edit on a trip; the form becomes edit mode. Save changes.
- Export: click Export JSON to download your trips as a file.
- Import: choose a JSON file created by this app to restore or merge trips.
- Clear All: deletes all local trips (confirm first).
Conclusion
You now have a complete, beginner-friendly travel itinerary planner JavaScript project you can run locally, publish in a server, or extend with features like reminders, calendar integrations, or export to CSV. This tutorial includes a full working example (single HTML file), clear code comments, and How it works travel itinerary planner javascript.
View Demo
