---
title: Custom calendar
tags: Projects
---
Back in the day, when dealing with date picker, I always go for ready made plugins on the internet, But the size of calendar is usually kind of small, it just lowers UX, so this time, I gonna show you guys how I build a customer calendar from stratch, let's started!!
## [example code](https://codepen.io/nienyingchou/pen/bGRqQpJ)
## Agenda
* Get browser language
* Put moment.js in that language
* Get weekday names
* Generate calendar arrays
* Build the layout
* Add functions (last month, next month, select date)
## Get browser language
* because the property of browser in mobile is not the same, so we have to detect if the user is using mobile
```javascript=
const isUsingMobile =
navigator.userAgent.match(/Android/i) ||
navigator.userAgent.match(/webOS/i) ||
navigator.userAgent.match(/iPhone/i) ||
navigator.userAgent.match(/iPad/i) ||
navigator.userAgent.match(/iPod/i) ||
navigator.userAgent.match(/BlackBerry/i) ||
navigator.userAgent.match(/Windows Phone/i);
const getBrowserLanguage = () => {
// default the locale to English
let lang = "en";
if (isUsingMobile) {
lang = navigator.languages
.find((x) => x.substr(0, 2) === navigator.language.substr(0, 2))
.substr(0, 2);
} else {
lang = navigator.language;
}
console.log("getBrowserLanguage", lang);
return lang;
};
```
## Put moment.js in that language
```javascript=
moment.locale(getBrowserLanguage());
```
## Get weekday names
```javascript=
moment.weekdaysMin(true);
```
## Generate calendar arrays
```javascript=
// generate calendar array according to selectDate
// return date array
const getCalendarArray = (selectDate) => {
// default to the beginning of the month
let startDate = moment(selectDate).format("01-MM-YYYY");
// default to the end of the month
const daysInMonth = moment(selectDate).daysInMonth();
let endDate = moment(selectDate).format(`${daysInMonth}-MM-YYYY`);
// modify startDate to the exact date
const firstDate = selectDate
.clone()
.startOf("month")
.startOf("week")
.date();
if (firstDate > 20) {
// means the date is from last month
const MMYYYY = moment(selectDate).subtract(1, "month").format("MM-YYYY");
startDate = moment(selectDate).format(`${firstDate}-${MMYYYY}`);
}
// modify endDate to the exact date
const lastDate = selectDate.clone().endOf("month").endOf("week").date();
if (lastDate < 10) {
// means the date is from next month
const MMYYYY = moment(selectDate).add(1, "month").format("MM-YYYY");
endDate = moment(selectDate).format(`0${lastDate}-${MMYYYY}`);
}
const date = [];
// format startDate and endDate
const formatStartDate = moment(startDate, dateFormat);
const formatEndDate = moment(endDate, dateFormat);
// push the dates between startDate and endDate into date array
for (
let m = moment(formatStartDate);
m.isSame(formatEndDate) || m.isBefore(formatEndDate);
m.add(1, "days")
) {
date.push(m.format(dateFormat));
}
const daysInWeek = 7;
// split the array by 7 (7, because 7 days in a week)
const dateArrayOfWeeks = [
...Array(Math.ceil(date.length / daysInWeek))
// eslint-disable-next-line
].map((_) => date.splice(0, daysInWeek));
console.log(dateArrayOfWeeks);
setDates(dateArrayOfWeeks);
};
```
## Build the layout
### calender body
```javascript=
<table>
{/* Calendar Header */}
<tr>
{weekdaysShort.map((weekday) => (
<th key={weekday}>{weekday}</th>
))}
</tr>
{/* Calendar Body */}
{dates.map((everyWeek) => (
<tr key={everyWeek[0]}>
{everyWeek.map((everyDay) => (
<td
onClick={() => handleSelectDate(everyDay)}
style={{
background:
moment(everyDay, dateFormat).format(dateFormat) ===
selectDate.format(dateFormat)
? "#CCC"
: "inherit",
color:
moment(everyDay, dateFormat).format("MM") ===
selectDate.format("MM")
? "inherit"
: "#CCC"
}}
>
{moment(everyDay, dateFormat).format("DD")}
</td>
))}
</tr>
))}
</table>
```
### calender control
```javascript=
<div className="flex">
<button onClick={lastMonth}>
<b>{"<"}</b>
</button>
<div>{selectDate.format("LL")}</div>
<button onClick={nextMonth}>
<b>{">"}</b>
</button>
</div>
```
## Add functions
```javascript=
const lastMonth = () => {
const newDate = moment(selectDate).subtract(1, "month");
updateData(newDate);
};
const nextMonth = () => {
const newDate = moment(selectDate).add(1, "month");
updateData(newDate);
};
// update calender when user click date
const handleSelectDate = (selectDate) => {
const newDate = moment(selectDate, dateFormat);
updateData(newDate);
};
const updateData = (newDate) => {
setSelectDate(newDate);
getCalendarArray(newDate);
};
```