Build a Simple Travel Planner — travel itinerary planner javascript

Build a Simple Travel Planner — travel itinerary planner javascript

 

Build a Simple Travel Planner

 

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

Add multiple trip items with: title (destination), start and end dates, and notes.
Edit and delete trips.
Sort trips by start date.
Filter trips by date range.
Persist trips in browser localStorage so they remain after refresh.
Export trips as JSON and import JSON to restore trips (manual backup).
Clean, accessible UI with clear buttons.

Bonus UX Features

Responsive layout for desktop and mobile.
Friendly date validation and helpful user messages.
Search/filter box to find trips quickly.

 

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.
index.html
<!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 localStorage under the key travel_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 tripId input 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 trips array 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 .json file 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, and endDate.

 

Deleting and duplicating

  • Each trip shows action buttons: Edit, Duplicate, and Delete.
  • Delete removes the trip from trips and updates localStorage. 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 .html file and run it.
  • The JavaScript is placed just before </body> and contains:
    • Utility helpers (qs, STORAGE_KEY, genId, formatDate).
    • loadTrips() and saveTrips() to read/write localStorage.
    • 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.

 

Usage instructions — quick start

  1. Save the code above as travel-planner.html.
  2. Open the file in any modern browser (Chrome, Firefox, Safari).
  3. Add a trip: fill destination, select start and end dates, optional notes — click Add Trip.
  4. Edit: click Edit on a trip; the form becomes edit mode. Save changes.
  5. Export: click Export JSON to download your trips as a file.
  6. Import: choose a JSON file created by this app to restore or merge trips.
  7. 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
Previous Post Next Post