Language:

How to Make a Simple HTML Calendar (Code and No-Code Options)

Four ways to put a calendar on your website — from copy-paste HTML and CSS code to a Google Calendar embed to a fully configured event calendar widget. Each method includes working examples, honest tradeoff notes, and a decision framework so you pick the right approach for your situation.
See what ChatGPT thinks Open the Live Widget Editor
How to Make a Simple HTML Calendar (Code and No-Code Options)

Many HTML calendar tutorials focus on the basics: a static monthly grid built with simple markup and styling. But for a live website calendar, you’ll usually want features like navigation, responsiveness, and event support so visitors can actually interact with it and stay up to date.

This article covers four approaches to adding an HTML calendar to your website: building a static month display with CSS Grid, adding JavaScript for dynamic navigation, embedding Google Calendar as an iframe, and using a no-code calendar widget. Each method fits a different situation, and the decision framework at the end helps you match the right one to yours.

What you’ll learn:

  • Copy-paste-ready HTML calendar code using CSS Grid — static and dynamic versions
  • How to add month navigation and today-highlighting with vanilla JavaScript
  • Google Calendar embed setup, including its limitations on mobile
  • No-code calendar widget setup with event management and filtering
  • A decision framework matching calendar methods to specific use cases

Methods Compared

Before committing to an approach, a quick comparison helps narrow down your options. The four methods differ in what they can do, how much setup they require, and who they’re built for:

MethodBest forDifficultyCustomizationDynamic eventsCost
Static HTML/CSSDisplay-only month gridBasic (copy-paste)Full (your CSS)NoFree
Dynamic JS calendarNavigable month calendarIntermediateFull (your code)Month navigationFree
Google Calendar embedOrganizations using Google CalendarEasy (iframe)Very limitedYes (syncs with Google)Free
No-code widgetEvent listings, filtering, RSVP linksEasy (no code)Full (visual editor)Yes (manual + Google sync)Free–paid

Takeaway: A static HTML/CSS calendar works well for simple visual layouts, such as portfolios, schedules, or design elements. Adding JavaScript enables month navigation and automatic date rendering. A Google Calendar embed is a good fit if you already manage events in Google and want a quick, low-maintenance display. A no-code HTML calendar widget adds more flexibility, including event management, recurring events, filtering, and visual customization without coding.

Method 1: Static HTML/CSS Calendar

This is the simplest HTML calendar code you can build — a single-month display using CSS Grid. It’s the foundation that Method 2 builds on, and it works as a standalone template if all you need is a visual month layout.

CSS Grid calendar example

The code uses CSS Grid with repeat(7, 1fr) for the seven-column day layout. Grid has roughly 95% global browser support, so compatibility isn’t a concern. Unlike older workflows that use HTML tables with deprecated attributes like cellspacing and bgcolor, Grid gives you a responsive layout with clean, modern markup.

Copy the code below and paste it into any HTML file or CMS code block. It renders a June 2026 calendar — change the month heading and day numbers to display a different month.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Static HTML Calendar</title>
<style>
  .calendar {
    --accent: #2563eb;          /* change this to match your brand */
    max-width: 480px;
    margin: 0 auto;
    font-family: system-ui, sans-serif;
  }
  .calendar-header {
    display: flex;
    justify-content: center;
    align-items: center;
    padding: 0.75rem 0;
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--accent);
  }
  .calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);  /* 7 equal columns */
    gap: 2px;
    text-align: center;
  }
  .day-name {
    padding: 0.5rem 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: #64748b;
  }
  .day {
    padding: 0.6rem 0;
    font-size: 0.95rem;
    border-radius: 6px;
  }
  .day:hover { background: #f1f5f9; }
  .today {
    background: var(--accent);
    color: #fff;
    font-weight: 600;
  }
  .today:hover { background: var(--accent); }
</style>
</head>
<body>

<div class="calendar">
  <div class="calendar-header">June 2026</div>
  <div class="calendar-grid">
    <span class="day-name">Mon</span>
    <span class="day-name">Tue</span>
    <span class="day-name">Wed</span>
    <span class="day-name">Thu</span>
    <span class="day-name">Fri</span>
    <span class="day-name">Sat</span>
    <span class="day-name">Sun</span>
    <!-- June 2026 starts on Monday — no offset needed -->
    <span class="day today">1</span>
    <span class="day">2</span><span class="day">3</span><span class="day">4</span>
    <span class="day">5</span><span class="day">6</span><span class="day">7</span>
    <span class="day">8</span><span class="day">9</span><span class="day">10</span>
    <span class="day">11</span><span class="day">12</span><span class="day">13</span>
    <span class="day">14</span><span class="day">15</span><span class="day">16</span>
    <span class="day">17</span><span class="day">18</span><span class="day">19</span>
    <span class="day">20</span><span class="day">21</span><span class="day">22</span>
    <span class="day">23</span><span class="day">24</span><span class="day">25</span>
    <span class="day">26</span><span class="day">27</span><span class="day">28</span>
    <span class="day">29</span><span class="day">30</span>
  </div>
</div>

</body>
</html>

The --accent CSS custom property at the top controls the highlight color — change it once, and the header and today-marker both update. The layout is responsive by default because 1fr units divide available space equally rather than using fixed pixel widths. On smaller screens, the cells shrink proportionally without breaking.

If the month you’re displaying doesn’t start on Monday, use grid-column-start on the first day cell to push it to the correct column. For example, if the month starts on Wednesday, add style="grid-column-start: 3" to the first <span class="day">. This positions the first day in the third column without requiring empty placeholder cells — one of the advantages of CSS Grid over a table-based layout.

Note: To turn this simple calendar HTML code into a reusable HTML calendar template, save the file and swap the month heading and day numbers whenever you need a different month. For anything beyond a single static display, Method 2 handles that automatically.

Method 2: Dynamic JavaScript Calendar

The static version works as a snapshot, but most practical use cases need month navigation and an automatically rendered current date. This version builds on the same CSS Grid structure and adds roughly 50 lines of vanilla JavaScript for dynamic rendering.

Dynamic JavaScript Calendar example

The calendar HTML code below detects the current month and year, renders the correct number of days, positions the first day in the right column, highlights today, and lets visitors navigate forward and backward between months. Year rollover (December to January, or January back to December) is handled automatically.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dynamic HTML Calendar</title>
<style>
  .calendar {
    --accent: #2563eb;
    max-width: 480px;
    margin: 0 auto;
    font-family: system-ui, sans-serif;
  }
  .calendar-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.75rem 0;
  }
  .calendar-header button {
    background: none;
    border: 1px solid #e2e8f0;
    border-radius: 6px;
    padding: 0.4rem 0.8rem;
    cursor: pointer;
    font-size: 0.9rem;
  }
  .calendar-header button:hover { background: #f1f5f9; }
  .month-year {
    font-size: 1.2rem;
    font-weight: 600;
    color: var(--accent);
  }
  .calendar-grid {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
    text-align: center;
  }
  .day-name {
    padding: 0.5rem 0;
    font-size: 0.8rem;
    font-weight: 600;
    color: #64748b;
  }
  .day {
    padding: 0.6rem 0;
    font-size: 0.95rem;
    border-radius: 6px;
  }
  .day:hover { background: #f1f5f9; }
  .today {
    background: var(--accent);
    color: #fff;
    font-weight: 600;
  }
  .today:hover { background: var(--accent); }
</style>
</head>
<body>

<div class="calendar">
  <div class="calendar-header">
    <button id="prev">&larr; Prev</button>
    <span class="month-year" id="monthYear"></span>
    <button id="next">Next &rarr;</button>
  </div>
  <div class="calendar-grid" id="grid">
    <span class="day-name">Mon</span>
    <span class="day-name">Tue</span>
    <span class="day-name">Wed</span>
    <span class="day-name">Thu</span>
    <span class="day-name">Fri</span>
    <span class="day-name">Sat</span>
    <span class="day-name">Sun</span>
  </div>
</div>

<script>
  const grid = document.getElementById("grid");
  const monthYear = document.getElementById("monthYear");
  const now = new Date();
  let currentMonth = now.getMonth();   // 0-based: 0 = January
  let currentYear = now.getFullYear();

  const monthNames = [
    "January","February","March","April","May","June",
    "July","August","September","October","November","December"
  ];

  function renderCalendar() {
    // Clear previous day cells, keep the 7 day-name headers
    grid.querySelectorAll(".day").forEach(d => d.remove());

    monthYear.textContent = monthNames[currentMonth] + " " + currentYear;

    // Day of week for the 1st (0=Sun). Convert to Mon-start: Mon=0 … Sun=6
    let firstDay = new Date(currentYear, currentMonth, 1).getDay();
    firstDay = (firstDay + 6) % 7;

    const daysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
    const today = new Date();

    // Blank cells before the first day
    for (let i = 0; i < firstDay; i++) {
      const blank = document.createElement("span");
      blank.className = "day";
      grid.appendChild(blank);
    }

    // Day cells
    for (let d = 1; d <= daysInMonth; d++) {
      const cell = document.createElement("span");
      cell.className = "day";
      cell.textContent = d;

      if (
        d === today.getDate() &&
        currentMonth === today.getMonth() &&
        currentYear === today.getFullYear()
      ) {
        cell.classList.add("today");
      }
      grid.appendChild(cell);
    }
  }

  document.getElementById("prev").addEventListener("click", () => {
    currentMonth--;
    if (currentMonth < 0) { currentMonth = 11; currentYear--; }
    renderCalendar();
  });

  document.getElementById("next").addEventListener("click", () => {
    currentMonth++;
    if (currentMonth > 11) { currentMonth = 0; currentYear++; }
    renderCalendar();
  });

  renderCalendar();
</script>

</body>
</html>

Two JavaScript details worth noting. First, new Date(year, month, 1).getDay() returns the weekday of the first day, but JavaScript uses Sunday as 0. The (firstDay + 6) % 7 conversion shifts the index so Monday is 0 and Sunday is 6, matching the grid’s Monday-start layout. Second, new Date(year, month + 1, 0).getDate() is a shorthand for getting the number of days in any month — day 0 of the next month returns the last day of the current one.

A common mistake in calendar JavaScript is forgetting that months are 0-based. January is 0, not 1. If your calendar shows the wrong month, this is almost always the reason. The navigation handlers also need to handle year rollover explicitly — decrementing past January should set the month to December and subtract a year.

Limitation: This dynamic calendar handles display and navigation, but not events, recurring schedules, or calendar sync. Features like event details, filtering, or saved schedules require additional infrastructure and code. If you need event management, Method 3 or 4, or a JavaScript calendar library, is usually a more practical option.

Method 3: Google Calendar Embed

If your organization already manages its schedule in Google Calendar, embedding it as an iframe skips the coding step entirely. The result is a live HTML event calendar that updates automatically whenever you add or change events in Google Calendar.

Setup process

  1. Make the calendar public. In Google Calendar, open Settings for the calendar you want to embed. Under “Access permissions for events,” check “Make available to public.” Only public calendars display in an embed — private or shared calendars won’t render.
  2. Open the embed settings. In the same settings panel, scroll to “Integrate calendar.” Click the “Customize” button to open the customization tool.
  3. Configure the display. The customization tool lets you set the title, default view (Month, Week, or Agenda), width and height in pixels, start date, timezone, and toggle individual elements like navigation buttons, the date header, print icon, and calendar tabs. You can also combine multiple public calendars into a single embed.
  4. Copy the iframe code. Click “Copy” to get the <iframe> embed code. It looks like this: <iframe src="https://calendar.google.com/calendar/embed?src=..." width="800" height="600"></iframe>
  5. Paste into your website. Add the iframe to any HTML page or CMS Custom HTML block. For a more detailed walkthrough, including platform-specific paste instructions, see this guide to adding Google Calendar to any website.

Tradeoffs

A Google Calendar embed works well for a quick, free calendar display, but it has limitations. The iframe uses fixed dimensions and isn’t fully responsive, which can make mobile viewing awkward.

Since it loads from Google’s domain, you can’t fully customize the design with your own CSS or JavaScript, so it will always look like an embedded Google product. Visitors can view events and open details, but interaction is limited to viewing rather than booking or RSVPing directly.

Note: These tradeoffs are fine for internal-facing pages, community websites, or any context where visual branding isn’t critical, and your audience is primarily on desktop. For anything public-facing where mobile experience and design consistency matter, a no-code widget gives you more control.

Method 4: No-Code Calendar Widget

When you need event management, visual customization, and responsive design without writing code, a dedicated calendar widget bridges the gap between a basic HTML calendar and a full calendar application. The Elfsight Event Calendar is one option in this category – and it only takes 5 minutes to set up. Here’s the process at a glance:

  1. Open the Calendar editor and select a template.
  2. Add your Events and configure the style.
  3. Click “Add to website” and copy the embed code.
  4. Paste the code into your website backend and publish.

Create your own Calendar right here in the interactive visual editor ↓

Standout features

Everything below is functionality that a hand-coded HTML/JS calendar doesn’t provide out of the box — and that would take significant development time to build from scratch.

  • Seven layout types — List, Grid, Masonry, Slider, and calendar views for Month, Week, and Day. A single widget can display different layouts on different pages.
  • Three event sources — add events manually through a visual editor, import in bulk via CSV, or sync one-way from Google Calendar (with support of venue, host, tags, event type, etc.).
  • Recurring events — daily, weekly, monthly, yearly, and custom frequencies with exception handling for individual occurrences.
  • Event popups — visitors click an event to see full details, a location map, CTA button, and an “Add to Calendar” option that generates a universal .ics file.
  • Five filters + search — date, event type, venue, host, and tags, plus a keyword search bar. Filters display inline or in a dropdown.
  • Visitor timezone adjustment — event times automatically adapt to each visitor’s local timezone, which matters for virtual or cross-timezone events.
Pro tip: The widget links to external pages for ticketing or registration but doesn’t process payments or manage RSVPs internally. Elfsight has a separate Appointment Booking widget for scheduling use cases

It works on WordPress, Shopify, Squarespace, Wix, Webflow, and any platform that supports a Custom HTML block. For the full step-by-step setup process, the guide to creating an event calendar for your website covers it in detail.

Which Method Fits Your Situation

The right method depends on what your calendar needs to do, not how technical you are. The table below maps common scenarios to the approach that best fits each one.

SituationBest methodWhy
Portfolio or personal website needing a month displayStatic HTML/CSSFull design control, no dependencies, minimal code
Website that needs a navigable calendar without eventsDynamic JS calendarMonth navigation and today-highlighting without a backend
Organization already using Google Calendar for schedulingGoogle Calendar embedSyncs automatically, zero code, zero maintenance
Event venue, gym, school, or church publishing event listingsNo-code widgetEvent management, filtering, recurring events, CTA buttons
Developer building a custom calendar applicationJS library (FullCalendar, etc.)Drag-and-drop, recurring event rules, API support
E-commerce website showing sale or launch datesStatic HTML/CSS or countdown widgetA full calendar is often overkill — a simple date display works
Freelancer or consultant displaying availabilityNo-code widget or booking toolAvailability display needs scheduling logic, not just a grid

The dividing line between coding a calendar and using a tool usually comes down to event management. A basic JavaScript calendar with month navigation is relatively straightforward, but features like recurring events, Google Calendar sync, timezone handling, filtering, or RSVP links quickly expand the scope. At that point, a widget or calendar library is often the more practical and time-efficient option.

Template Catalog

Explore 30+ Event Calendar templates

Check out more of the ready-made templates for any use case or build your own one!
HTML Interactive Calendar template
Embed an engaging calendar on your website to make users feel longing to come back.
Create a handy calendar for your website to optimize a scheduling process.
HTML Conference Agenda template
Add an agenda to your website to showcase conference speakers’ schedules and session times.
Embed a church’s schedule on your website to spread the word about upcoming events and services.
Embed a calendar on your website to let users stay up-to-date about all your business' recurring events.
No Suitable Template?
You can easily assemble the widget you need using our simple-to-use configurator.

JavaScript Calendar Libraries

If vanilla JavaScript isn’t enough but a no-code widget doesn’t fit your development stack, a handful of open-source libraries sit in the middle. These give you pre-built calendar components with event support, and you control the integration.

LibraryKey strengthSize / adoptionLicense
FullCalendarFull event management, drag-and-drop, plugin system19k+ GitHub stars, 1M+ npm weekly downloadsMIT (Standard)
TUI CalendarMulti-calendar support, strong Asian localization8.2k GitHub starsMIT
Vanilla Calendar ProLightweight, zero dependencies, built-in accessibilityLightweight / growingMIT
Event Calendar (vkurko)FullCalendar-like API at 37kb compressedSvelte-based, framework-agnosticMIT

FullCalendar is the default recommendation for developers who need a production-ready calendar API with event handling, recurring events, and framework support (React, Vue, Angular). For simpler needs, Vanilla Calendar Pro provides built-in ARIA labels, keyboard navigation, and a smaller footprint.

Accessibility and Mobile Tips

Most guides on how to create a calendar in HTML skip accessibility entirely. Adding a few attributes to your calendar code makes it usable for screen readers and keyboard-only visitors, and the effort is minimal compared to the impact.

For the code-based calendars in Methods 1 and 2, these additions cover the fundamentals:

  • Add aria-label=”June 2026″ (or the current month/year) to the calendar grid container so screen readers announce what the calendar displays
  • Use the abbr attribute on day-name headers: <span class=”day-name” abbr=”Wednesday”>Wed</span>. Screen readers can then announce the full day name instead of the abbreviation
  • For the interactive JavaScript calendar, add role=”grid” to the grid container and role=”gridcell” to each day cell. The W3C APG date picker pattern defines the full interaction model, including arrow-key navigation between days and Page Up/Down for month changes
  • Mark the current date with aria-current=”date” in addition to the visual .today class

ARIA disclaimer

One caution with ARIA: only add attributes you understand. Analysis of the top million web pages found that pages using ARIA averaged 59.1 detectable accessibility errors, compared to 42 on pages without it. Incorrect ARIA markup creates more barriers than no ARIA at all.

If you’re building a display-only calendar, a native HTML <table> with proper <th> headers is the most accessible option by default — the “first rule of ARIA” is to prefer native HTML semantics when they exist.

CSS Grid for mobile

For mobile, the CSS Grid code in both methods is responsive by default because fr units divide available space proportionally. On very small screens (under 360px), two adjustments help: abbreviate day names to single letters (M, T, W, T, F, S, S) and reduce cell padding to 0.35rem. Test on a real device before publishing — emulators don’t always reflect how touch targets feel at small sizes.

Frequently Asked Questions


Can I build an HTML calendar without JavaScript?

Yes. Method 1 in this article is a pure HTML and CSS calendar using CSS Grid — no JavaScript required. It displays a single month as a static grid. You manually set the month name and day numbers in the HTML. For navigation between months or automatic date rendering, JavaScript is needed (Method 2).

Is CSS Grid or an HTML table better for a calendar layout?

CSS Grid is the recommended approach for new calendar implementations. It handles the 7-column day layout natively with repeat(7, 1fr), makes responsive design automatic through fractional units, and doesn’t require empty cells for padding — grid-column-start positions the first day directly. HTML tables still work but rely on patterns from the early 2000s, and many tutorials using tables include deprecated attributes.

How do I make an HTML calendar responsive on mobile?

Use CSS Grid with fr units instead of fixed pixel widths — the code in this article does this by default. On very small screens, abbreviate day names to single letters and reduce cell padding. Avoid setting a fixed width on the calendar container; use max-width instead so the calendar can shrink on narrow viewports.

Can I add events to an HTML calendar without a backend?

A basic HTML and JavaScript calendar doesn’t support event storage or management — that requires a database, server-side code, and an admin interface. For event functionality without building a backend, a no-code widget like the Elfsight Event Calendar or a Google Calendar embed are more practical options. JavaScript calendar libraries like FullCalendar can also handle events if you’re comfortable writing code.

What's the difference between an HTML calendar and a calendar widget?

An HTML calendar is code you write and maintain — you control everything but you also build everything. A calendar widget is a pre-built tool with event management, recurring events, filtering, and design options that you configure through a visual editor and embed on your website via a code snippet. Widgets handle the complexity that would take significant development effort to build from scratch.

Where to Start

The right approach depends on what your calendar needs to do. A display-only month grid takes 30 minutes with the code in this article. A navigable calendar with today-highlighting takes slightly longer. An event calendar with filtering, recurring events, and RSVP links takes a different kind of tool entirely — and recognizing that boundary early saves more time than any amount of code optimization.

Start with the simplest method that covers your actual requirements. If you outgrow it, the next step is clear: a calendar widget you can embed without rebuilding from scratch, or a JavaScript library if you need full programmatic control. Either way, you’re building on a foundation that fits the job — not forcing a simple HTML calendar to do something it was never designed for.

Article by
Content Manager
I'm a content manager at Elfsight, creating practical guides on smart website solutions to help users improve their online projects. My articles are written to be simple, showing how widgets can boost websites and make them more effective.