try it
here's a live, working copy of the widget, generated right now by the exact code you're about to learn:
| S | M | T | W | T | F | S |
|---|
before we start — what is javascript actually doing here?
html builds the structure of a page (the boxes, the text, the table). css makes it pretty. javascript is the part that can think and react; it can check what today's date is, do a bit of math, and then write html into the page for us. so instead of typing out "20" by hand every day, we're going to write a small set of instructions that figures it out on its own, forever.
we'll build this in five small steps. none of them are long, and each one builds directly on the last, so if something doesn't click right away, keep reading! it usually clicks a step or two later.
step 1 — the html shell
we don't ask javascript to build the whole calendar from nothing. we give it a little bit of html to start with, and it only fills in the blanks. by hand, we write the table headers (the row of letters S M T W T F S) and two empty containers — one for the month name, one for the days — with an id attribute on each so javascript can find them later:
<div class="cal-month" id="cal-month"></div>
<table class="cal">
<thead>
<tr><th>S</th><th>M</th><th>T</th><th>W</th><th>T</th><th>F</th><th>S</th></tr>
</thead>
<tbody id="cal-body"></tbody>
</table>
id as a name tag on an html element. it doesn't change how the element looks, it just gives javascript a way to say "grab that specific element" instead of guessing. we named ours cal-month and cal-body so they're easy to recognize in the code below.step 2 — get today's date info
javascript has a built-in tool called Date that already knows what day it is on the visitor's computer — we don't need to type today's date in ourselves. we just ask it:
const currentDate = new Date();
const currentDay = currentDate.getDate(); // e.g. 20
const currentMonth = currentDate.getMonth(); // 0-indexed! 0 = january
const currentYear = currentDate.getFullYear(); // e.g. 2026
let's break that down line by line, since it looks like a lot at once if you're new to this:
constjust means "make a new named box to store a value in, and don't let that box be reassigned later."currentDate,currentDay, etc. are the names we're choosing for those boxes — you could name them anything, but clear names make code easier to read later.new Date()creates a snapshot of the exact current date and time. we store that whole snapshot insidecurrentDate..getDate(),.getMonth(), and.getFullYear()are little built-in questions we can ask that snapshot..getDate()asks "what day of the month is it?",.getMonth()asks "which month?", and.getFullYear()asks "which year?"- the
//parts aren't code at all — they're comments. anything after//on a line is ignored by javascript completely; they're just notes left for humans reading the code.
getMonth() counts starting from 0, so january is 0 and december is 11, not 1–12 like we'd normally say out loud. this trips up almost everyone the first time. we'll use this same 0-based counting later on when we build our list of month names, so they'll line up correctly.step 3 — figure out the month's shape
to draw a calendar grid properly, we need two more pieces of information: which weekday the 1st of the month falls on (so we know how far to indent the first row), and how many days the month has in total (so we know when to stop). javascript can figure both of these out for us too, using a clever little trick:
// which weekday (0=Sunday...6=Saturday) the 1st falls on
const firstDayOfMonth = new Date(currentYear, currentMonth, 1).getDay();
// passing day "0" of *next* month rolls back to the last day of *this* month
const totalDaysInMonth = new Date(currentYear, currentMonth + 1, 0).getDate();
walking through this slowly:
new Date(currentYear, currentMonth, 1)builds a brand new date object for "day 1 of the current month" — not today's date, just the 1st. calling.getDay()on it (note:getDay, notgetDate— javascript sadly uses very similar names for different things here) tells us which weekday that is, as a number from 0 (sunday) to 6 (saturday).- the second line is a neat shortcut: if you ask javascript for "day 0" of a month, it doesn't error out, it just quietly rolls back to the last day of the previous month. so by asking for day 0 of next month, we actually get the last day of this month, which is exactly the total number of days we need.
you don't need to memorize this trick! just know that it exists, since you'll see it reused in lots of calendar-building code.
step 4 — build the table rows
now for the main part: turning those numbers into actual table rows. the approach here is to build up one long piece of html as plain text, piece by piece, and only insert it into the page once we're done. think of it like writing a letter on scratch paper before copying it into the final envelope.
let calendarHTML = '<tr>';
let weekDay = 0;
// pad the first row so day 1 lands on the correct weekday
for (let i = 0; i < firstDayOfMonth; i++) {
calendarHTML += '<td></td>';
weekDay++;
}
// one cell per day of the month
for (let day = 1; day <= totalDaysInMonth; day++) {
if (day === currentDay) {
calendarHTML += `<td class="today">${day}</td>`;
} else {
calendarHTML += `<td>${day}</td>`;
}
weekDay++;
if (weekDay === 7) {
calendarHTML += '</tr><tr>';
weekDay = 0;
}
}
calendarHTML += '</tr>';
this is the densest part of the tutorial, so let's slow way down:
let calendarHTML = '<tr>';starts our scratch paper with an opening table-row tag. we useletinstead ofconsthere because, unlike step 2's values, this one will change repeatedly as we build it up.let weekDay = 0;is a little counter we'll use to keep track of which column (sunday through saturday) we're currently filling in.- a
forloop is just a way of telling javascript "repeat this block of instructions a certain number of times." the first loop repeatsfirstDayOfMonthtimes, adding one empty cell (<td></td>) each time — this pushes the 1st of the month over to sit under the correct weekday column, and pads out any days from the "previous month" that would otherwise show up empty. - the second loop counts from
day = 1up to the last day of the month. for every single day, it adds one table cell containing that day's number. - inside that loop,
if (day === currentDay)checks: "is this the day we're currently on, today?" if yes, the cell gets a special class,class="today", which the css uses to visually highlight it. if not, it gets a plain cell. - the backtick-quoted text like
`<td class="today">${day}</td>`is called a template literal. the special part is${day}— anything inside${ }gets swapped out for the actual value of that variable. so ifdayis currently14, that whole chunk becomes the text<td class="today">14</td>. - every time we place a cell,
weekDay++increases our column counter by one (++is just shorthand for "add 1"). once it hits7(meaning we've filled sunday through saturday), we close the current row, open a new one, and reset the counter back to0to start the next week. - after both loops finish, we add one final
</tr>to close off the last row.
createElement for that. but for something small that only needs to be built once, like this calendar, gluing together one long piece of html text is simpler to read and just as fast. if you were building something that updates constantly (like a live chat), createElement would usually be the safer, more controlled choice — but that's a topic for another tutorial.step 5 — insert it into the page
we now have a finished piece of html sitting inside the calendarHTML variable, but it's still just text — the page hasn't actually shown it yet. this last step hands it over to the two containers we set up back in step 1:
const monthNames = ["january","february","march","april","may","june",
"july","august","september","october","november","december"];
document.getElementById('cal-month').textContent =
`${monthNames[currentMonth]} ${currentYear}`;
document.getElementById('cal-body').innerHTML = calendarHTML;
monthNamesis a list (called an "array" in javascript) of all twelve month names, written out in order starting from january at position0— which is exactly why step 2's 0-based counting matters here.monthNames[currentMonth]looks up the name at that position, so ifcurrentMonthis7, it grabs"august".document.getElementById('cal-month')is javascript's way of saying "find the html element with the idcal-month", the one we tagged back in step 1. setting its.textContentwrites plain text into it, in this case the month name and year.document.getElementById('cal-body').innerHTML = calendarHTML;does the same lookup for our table body, but uses.innerHTMLinstead of.textContent. this matters becausecalendarHTMLcontains real html tags (like<tr>and<td>) that we want the browser to actually build into table rows, not just display as plain text on the page.
result
put together, these five small steps give you a calendar that always shows the correct current month and highlights today, with zero manual updates! the whole thing runs automatically the moment the page loads, using nothing but the visitor's own computer clock.
if you want to practice, try changing class="today" to highlight a different, fixed date instead. that small tweak alone will help a lot of this click into place.