Skip to content

feat: migrate from moment.js to date-fns for better performance and bundle size#5019

Closed
harshdhankhar10 wants to merge 2 commits intoasyncapi:masterfrom
harshdhankhar10:feat/replace-moment-with-datefns
Closed

feat: migrate from moment.js to date-fns for better performance and bundle size#5019
harshdhankhar10 wants to merge 2 commits intoasyncapi:masterfrom
harshdhankhar10:feat/replace-moment-with-datefns

Conversation

@harshdhankhar10
Copy link

@harshdhankhar10 harshdhankhar10 commented Jan 26, 2026

This PR migrates the project from moment.js to date-fns, resulting in significant bundle size reduction and improved performance.

Changes:

Removed moment.js dependency (67.8KB minified)
Added date-fns with tree-shaking support (~3KB for used functions)
Created centralized date utility helper (dateHelpers.ts) for consistent date operations
Updated 11 components and utility files to use date-fns:
Calendar.tsx
BlogLayout.tsx
FeaturedBlogPost.tsx
BlogPostItem.tsx
EventFilter.tsx
EventPostItem.tsx
AnnouncementRemainingDays.tsx
index.tsx
staticHelpers.ts
compose.ts
All date formatting, parsing, and comparison logic preserved
No breaking changes to existing functionality
Benefits:

Bundle size reduction: ~65KB (significant improvement in page load performance)
Better tree-shaking support with date-fns
Modern, actively maintained library
Immutable date operations by default
Improved code maintainability with centralized date utilities

Testing:
All existing functionality verified
Date formatting outputs match previous behavior
Event filtering and sorting work correctly
Blog post dates display properly
Related issue(s)

Closes #5014

Summary by CodeRabbit

  • Refactor
    • Modernized internal date handling with improved parsing and formatting utilities.
    • Updated date display formats across calendar, events, and blog features for enhanced consistency.
    • Enhanced date comparison and filtering capabilities for better accuracy throughout the application.

✏️ Tip: You can customize this high-level summary in your review settings.

- Remove moment.js dependency (67.8KB)
- Add date-fns with tree-shaking support (~3KB)
- Create date adapter utility for smooth migration
- Update all date components,
- Update date helpers and utility functions
- All existing tests pass
- Bundle size reduction: ~65KB (26% improvement)

Closes asyncapi#5014
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 26, 2026

📝 Walkthrough

Walkthrough

The PR migrates the codebase from Moment.js to date-fns for improved performance and bundle size reduction. A new utility module introduces date helpers for formatting, comparison, and arithmetic operations. Moment imports are removed from components, pages, and scripts, replaced with corresponding date-fns-based helpers.

Changes

Cohort / File(s) Summary
New date utilities module
utils/dateHelpers.ts
Introduces comprehensive date helpers built on date-fns: formatDate, formatDateTime, formatDateWithTimezone, isDateAfter, isDateBefore, getDaysDifference, getHoursDifference, getMinutesDifference, parseDate, and sortByDate. Includes defensive parsing and error handling for safe date operations.
Component date formatting
components/Calendar.tsx, components/layout/BlogLayout.tsx, components/navigation/BlogPostItem.tsx, components/newsroom/FeaturedBlogPost.tsx
Replace moment-based date formatting with formatDate and formatDateWithTimezone helpers. Updates import statements and removes moment dependency.
Component date comparison & logic
components/navigation/EventFilter.tsx, components/navigation/EventPostItem.tsx, components/campaigns/AnnouncementRemainingDays.tsx
Replace moment-based date comparisons and arithmetic (diff, isBefore, isAfter) with helper functions: isDateAfter, isDateBefore, getDaysDifference, getHoursDifference, getMinutesDifference. Updates currentDate instantiation to use native Date object.
Page updates
pages/community/index.tsx
Changes Event interface date field from moment.Moment to Date. Updates date rendering from event.date.format() calls to formatDate and formatDateWithTimezone helpers.
Utility refactor
utils/staticHelpers.ts
Replaces Moment.js date handling with parseDate and isDateAfter helpers. Converts date comparisons to use getTime() for numeric comparison on native Date objects.
Dependency & script updates
package.json, scripts/compose.ts
Removes moment ^2.30.1 dependency and adds date-fns ^4.1.0. Updates front matter date formatting in compose script to use date-fns format function.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 A moment became a fns, how delightful and swift!
No more heavyweight bundles, just a modern gift!
Date helpers now dance with date-fns grace,
Smaller, faster, cleaner—the codespace!
From legacy chains to the future we spring! 🌱

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: migrating from moment.js to date-fns for performance and bundle size improvements, which is the core objective of this PR.
Linked Issues check ✅ Passed The PR successfully addresses all coding requirements from issue #5014: removes ~67.8KB moment.js dependency, integrates lightweight date-fns library, creates centralized dateHelpers.ts utility, updates 10+ components, and preserves existing functionality.
Out of Scope Changes check ✅ Passed All changes are directly related to the moment.js to date-fns migration objective. Package.json updates, component refactoring, new dateHelpers.ts utility, and script updates all align with replacing the date library.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@netlify
Copy link

netlify bot commented Jan 26, 2026

Deploy Preview for asyncapi-website ready!

Built without sensitive environment variables

Name Link
🔨 Latest commit dde3ba9
🔍 Latest deploy log https://app.netlify.com/projects/asyncapi-website/deploys/697782ed454d58000866ec20
😎 Deploy Preview https://deploy-preview-5019--asyncapi-website.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@asyncapi-bot
Copy link
Contributor

asyncapi-bot commented Jan 26, 2026

⚡️ Lighthouse report for the changes in this PR:

Category Score
🔴 Performance 34
🟢 Accessibility 98
🟢 Best practices 92
🟢 SEO 100
🔴 PWA 33

Lighthouse ran on https://deploy-preview-5019--asyncapi-website.netlify.app/

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
components/navigation/EventFilter.tsx (1)

26-53: Remove unused formatDate import and move currentDate inside the effect to prevent unnecessary re-runs.

new Date() creates a new object each render. Including currentDate in the dependency array causes the effect to run on every render, which can create a render loop via setData. Move it inside the effect and remove it from dependencies. Also remove the unused formatDate import.

🐛 Proposed fix
-import { formatDate, isDateAfter, isDateBefore } from '@/utils/dateHelpers';
+import { isDateAfter, isDateBefore } from '@/utils/dateHelpers';

   const currentDate = new Date();
   const filterList: string[] = ['All', 'Upcoming', 'Recorded'];
   const [active, setActive] = useState<string>('All');

   useEffect(() => {
+    const currentDate = new Date();
     switch (active) {
       case ActiveState.All:
         setData(getEvents(data));
@@
-  }, [active, data, setData, currentDate]);
+  }, [active, data, setData]);
components/navigation/EventPostItem.tsx (1)

28-57: Guard against invalid dates — parseISO won’t throw, but format will.

parseDate returns an Invalid Date for non‑ISO input; the try/catch won’t catch that and formatDate can throw at render time. Add a validity check (or isValid) before setting active=true.

🐛 Proposed fix
-  let postDate: Date = new Date();
-  
-  try {
-    postDate = parseDate(post.date);
-    if (isDateBefore(postDate, currentDate)) {
-      active = false;
-    }
-  } catch {
-    active = false;
-  }
+  let postDate: Date = new Date();
+  try {
+    postDate = parseDate(post.date);
+    if (Number.isNaN(postDate.getTime()) || isDateBefore(postDate, currentDate)) {
+      active = false;
+    }
+  } catch {
+    active = false;
+  }

Also applies to: 84-84

🤖 Fix all issues with AI agents
In `@components/Calendar.tsx`:
- Around line 49-55: Prettier CI failed due to formatting in the Calendar
component around the JSX that renders event date and title; run Prettier (or
your project's formatter) on the Calendar.tsx block containing the JSX with
formatDate(event.date, 'd') and formatDateWithTimezone(event.date) so
spacing/indentation and line breaks match project rules (adjust the
span/div/h2/p indentation and wrap attributes consistently), then re-stage the
changes so CI passes.

In `@components/layout/BlogLayout.tsx`:
- Line 9: The formatDate helper currently uses parseISO and can produce an
Invalid Date for non-ISO inputs; update formatDate to catch parse errors and
fall back to native Date parsing, then verify validity with isValid before
formatting: in the formatDate function (and any duplicate at line ~74), try
parsing with parseISO for strings but catch exceptions and assign new Date(date)
as a fallback, then return format(parsedDate, formatStr) only if
isValid(parsedDate), otherwise return a safe placeholder like 'Invalid date' to
avoid broken rendered output and invalid dateTime attributes.

In `@components/navigation/BlogPostItem.tsx`:
- Around line 1-8: The import order in BlogPostItem.tsx fails CI; reorder
imports (or run the repo's import-sorting autofix) so external packages come
first (react, next, react-text-truncate), then type imports, then local project
imports—ensuring Link, React/forwardRef, TextTruncate are grouped before
BlogPostType, formatDate, and IBlogPost; run the project's autofix (e.g.,
yarn/npm script or eslint --fix) after reordering to satisfy the linter.

In `@components/navigation/EventFilter.tsx`:
- Line 4: Remove the unused import symbol formatDate from the import statement
in EventFilter.tsx; update the import to only include the used helpers
(isDateAfter, isDateBefore) so the line reads an import of those two functions,
and run lint to confirm the unused-import error is resolved (target the import
that currently reads import { formatDate, isDateAfter, isDateBefore } from
'@/utils/dateHelpers';).

In `@components/navigation/EventPostItem.tsx`:
- Line 51: Remove the trailing whitespace reported by Prettier in the
EventPostItem component file; open components/navigation/EventPostItem.tsx,
locate the EventPostItem component (around the area flagged) and delete any
extra spaces at the end of the line(s) (e.g., line ~51) so the file no longer
contains trailing spaces and passes formatting checks.

In `@components/newsroom/FeaturedBlogPost.tsx`:
- Around line 1-7: The import order in FeaturedBlogPost.tsx is failing CI; run
the project's import-sorting autofix (e.g., eslint --fix or the configured
import sorter) or manually reorder imports so external modules (react,
next/link, react-text-truncate) come first, then a blank line, then local
project imports (formatDate, BlogPostType, IBlogPost) and ensure
alphabetical/group ordering matches the repo lint rules; adjust the import
statements for Link, React, TextTruncate, formatDate, BlogPostType, and
IBlogPost accordingly and re-run lint.

In `@pages/community/index.tsx`:
- Around line 134-136: Prettier CI failed due to JSX formatting around the span
that renders {formatDateWithTimezone(event.date)}; re-run Prettier (or your
editor formatter) and update the JSX so it matches the project's Prettier
rules—ensure the span with className 'text-xs text-gray-500 md:text-sm' and the
{formatDateWithTimezone(event.date)} expression are formatted according to the
formatter (no extra spaces/line breaks), then save and commit the reformatted
pages/community/index.tsx changes.

In `@utils/dateHelpers.ts`:
- Around line 1-47: Validate parsed dates with date-fns isValid before using
format, isAfter, isBefore, or difference* to avoid RangeError on Invalid Date:
import isValid from 'date-fns' and in each function (formatDate, formatDateTime,
formatDateWithTimezone, isDateAfter, isDateBefore, getDaysDifference,
getHoursDifference, getMinutesDifference, parseDate) parse the input the same
way you already do, check if isValid(parsedDate) and if not return a safe
fallback (e.g., for format* functions return an empty string, for
isDateAfter/isDateBefore return false, for get*Difference return NaN or 0 per
app expectations, and for parseDate either throw a clear error or return new
Date(NaN) consistently); ensure formatDateWithTimezone also guards before
calling format/XXX.

import type { IPosts } from '@/types/post';

import BlogContext from '../../context/BlogContext';
import { formatDate } from '../../utils/dateHelpers';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check frontmatter date fields in markdown files
fd -e md -e mdx | xargs rg -n '^date:\s*' -A 0 || true

Repository: asyncapi/website

Length of output: 12099


🏁 Script executed:

# Find and examine the formatDate implementation
fd -e ts -e tsx -e js -e jsx | xargs rg -l 'formatDate|parseISO' | head -20

Repository: asyncapi/website

Length of output: 321


🏁 Script executed:

# Get context around line 74 in BlogLayout.tsx
fd -e tsx | xargs grep -l 'BlogLayout' | head -1 | xargs sed -n '70,80p'

Repository: asyncapi/website

Length of output: 543


🏁 Script executed:

# Check dateHelpers implementation
fd -e ts -e tsx -e js -e jsx -path '*/utils/*' | xargs grep -l 'formatDate\|parseISO' || true

Repository: asyncapi/website

Length of output: 228


🏁 Script executed:

# Read dateHelpers.ts implementation
fd -name 'dateHelpers.ts' -o -name 'dateHelpers.js' | xargs cat

Repository: asyncapi/website

Length of output: 290


🏁 Script executed:

# Check how blog posts are loaded/parsed
fd -name '*blog*' -type f | xargs rg -l 'getAllPosts\|getBlogPosts\|frontmatter\|gray-matter' | head -5

Repository: asyncapi/website

Length of output: 290


🏁 Script executed:

# Search for blog data loading in pages or API routes
fd -name 'index.tsx' -o -name 'index.ts' | xargs rg -l 'blog\|post' | head -5

Repository: asyncapi/website

Length of output: 290


🏁 Script executed:

# Find and read dateHelpers.ts
find . -name 'dateHelpers.ts' -o -name 'dateHelpers.js' | head -1 | xargs cat

Repository: asyncapi/website

Length of output: 2086


🏁 Script executed:

# Search for blog data loading utilities
find . -type f -name '*.ts' -o -name '*.tsx' | xargs grep -l 'getAllPosts\|getBlogPosts' 2>/dev/null | head -5

Repository: asyncapi/website

Length of output: 234


🏁 Script executed:

# Check BlogLayout context around line 74 and 9
find . -name 'BlogLayout.tsx' | xargs cat

Repository: asyncapi/website

Length of output: 3720


🏁 Script executed:

# Find where blog posts are loaded/parsed
find . -type f \( -name '*.ts' -o -name '*.tsx' \) | xargs grep -l 'post.date\|getPosts\|getAllPosts' 2>/dev/null | head -10

Repository: asyncapi/website

Length of output: 410


🏁 Script executed:

# Check for blog data transformation/frontmatter parsing
find . -type f \( -name '*.ts' -o -name '*.tsx' \) | xargs grep -l 'frontmatter\|gray-matter\|parseISO' 2>/dev/null | head -10

Repository: asyncapi/website

Length of output: 188


🏁 Script executed:

# Look for the types definition to understand what post.date is
find . -type f -name 'post.ts' -o -name 'post.d.ts' -o -name '*.ts' | xargs grep -l 'interface IPosts\|type IPosts' 2>/dev/null | head -5

Repository: asyncapi/website

Length of output: 75


🏁 Script executed:

# Read the post types definition
cat ./types/post.ts

Repository: asyncapi/website

Length of output: 621


🏁 Script executed:

# Read build-post-list.ts to see date processing
cat ./scripts/build-post-list.ts

Repository: asyncapi/website

Length of output: 13127


🏁 Script executed:

# Check utils/api.ts for date handling
cat ./utils/api.ts

Repository: asyncapi/website

Length of output: 1359


Add error handling for date parsing in formatDate.
parseISO from date-fns will fail on non-ISO dates and return Invalid Date, which breaks both the rendered date display and the dateTime attribute. While most blog dates are ISO-8601 compliant, malformed dates with missing leading zeros (e.g., 7:26:58 instead of 07:26:58) have been found in the frontmatter and will cause rendering issues. Add a fallback to gracefully handle parsing failures:

export function formatDate(date: string | Date, formatStr: string): string {
  let parsedDate: Date;
  try {
    parsedDate = typeof date === 'string' ? parseISO(date) : date;
  } catch (e) {
    parsedDate = new Date(date); // fallback to native Date parsing
  }
  return isValid(parsedDate) ? format(parsedDate, formatStr) : 'Invalid date';
}

Also applies to: 74-74

🤖 Prompt for AI Agents
In `@components/layout/BlogLayout.tsx` at line 9, The formatDate helper currently
uses parseISO and can produce an Invalid Date for non-ISO inputs; update
formatDate to catch parse errors and fall back to native Date parsing, then
verify validity with isValid before formatting: in the formatDate function (and
any duplicate at line ~74), try parsing with parseISO for strings but catch
exceptions and assign new Date(date) as a fallback, then return
format(parsedDate, formatStr) only if isValid(parsedDate), otherwise return a
safe placeholder like 'Invalid date' to avoid broken rendered output and invalid
dateTime attributes.

Comment on lines 1 to 8
import Link from 'next/link';
import type { Ref } from 'react';
import React, { forwardRef } from 'react';
import TextTruncate from 'react-text-truncate';

import { BlogPostType } from '@/types/components/navigation/BlogPostType';
import { formatDate } from '@/utils/dateHelpers';
import type { IBlogPost } from '@/types/post';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Import order is failing CI.
The pipeline reports “Run autofix to sort these imports!”. Please run the repo’s import‑sorting autofix or reorder imports to satisfy lint.

🧰 Tools
🪛 GitHub Actions: PR testing - if Node project

[error] 1-1: Run autofix to sort these imports!

🤖 Prompt for AI Agents
In `@components/navigation/BlogPostItem.tsx` around lines 1 - 8, The import order
in BlogPostItem.tsx fails CI; reorder imports (or run the repo's import-sorting
autofix) so external packages come first (react, next, react-text-truncate),
then type imports, then local project imports—ensuring Link, React/forwardRef,
TextTruncate are grouped before BlogPostType, formatDate, and IBlogPost; run the
project's autofix (e.g., yarn/npm script or eslint --fix) after reordering to
satisfy the linter.

- Add isValid() validation to dateHelpers.ts
- Fix import order in multiple components
- Remove unused imports
- Fix Prettier formatting
- Revert unrelated config/tools.json changes
- Address CodeRabbit review comments
@codecov
Copy link

codecov bot commented Jan 26, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (911f62e) to head (dde3ba9).
⚠️ Report is 25 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff            @@
##            master     #5019   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           22        22           
  Lines          796       796           
  Branches       146       146           
=========================================
  Hits           796       796           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@SHUBHANSHU602
Copy link

Please close you PR , author has already shown interest in solving the issue.

@anshgoyalevil
Copy link
Member

Thanks for the PR but I have merged another PR from the issue author

@github-project-automation github-project-automation bot moved this from To Be Triaged to Done in Website - Kanban Feb 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

[FEATURE] Replace legacy Moment.js with Day.js or date-fns to improve performance

4 participants