React-dates: Switch from moment to date-fns?

Created on 6 Dec 2016  Â·  52Comments  Â·  Source: airbnb/react-dates

I don't know what it would take to do, but reducing dependencies on moment would be a great thing.

https://date-fns.org/

feature request question

Most helpful comment

There are some reasons on this issue: https://github.com/date-fns/date-fns/issues/275

The primary reason for me is the size of moment with or without its locales (I hack them out using webpack, but moment is still pretty big). If you don't need all of the functionality from date-fns you can just use the functions you need, making the size even smaller. You could potentially get rid of the peerDependency and simply bundle the functionality you need.

All 52 comments

@aaronjensen I wouldn't assume folks are familiar with this library. It would probably help if you outlined the advantages of this library over moment (and in the context of react-dates).

There are some reasons on this issue: https://github.com/date-fns/date-fns/issues/275

The primary reason for me is the size of moment with or without its locales (I hack them out using webpack, but moment is still pretty big). If you don't need all of the functionality from date-fns you can just use the functions you need, making the size even smaller. You could potentially get rid of the peerDependency and simply bundle the functionality you need.

If date-fns gains traction in the community, then it might be reasonable to support both moment and date-fn objects - although that wouldn't make the dependencies any smaller.

The author of date-fns points out some reasons in https://github.com/date-fns/date-fns/issues/275#issuecomment-264934189 that we wouldn't want to support it just yet - and when it did have those capabilities, there's no reason not to switch over to it completely.

In general, nobody doesn't need "every locale known to humans" - because every locale you fail to account for, is a human you're excluding. That choice goes against the spirit of both the project and Airbnb.

I would be very interested in this, date-fns has gained allot of traction and looking forward seems to have allot more momentum and a better footing.

@ljharb date-fns supports 32 locales now with more coming. It's also quickly gaining traction in the community.

When it supports every locale that moment does, and passes all of moments test cases, then it is worth having this conversation again. :-)

Agreed. Although, without digging too much @ljharb do you think there is allot of work to swap out implementations?

That would depend on the moment APIs we're using, and how date-fns' API compares - assuming it's a change we want to make.

@andrewmclagan Just browsed through the moment usage in this project (I'm also interested in this swap) and it's very minimal (IMO), should be a relatively easy swap I think... Might make a fork to try it out.

FWIW Moment.js is great but just huge, and if we're talking about "excluding humans", being data-usage-savvy is also a super important consideration.

Its importance is why this issue remains open :-).

I created a branch of react-dates that uses date-fns in place of moment _(without implementing the locale logic)_. Whereas moment is a ~130kb bundle (without locales), this version only uses 52kb of date-fns. That number might be further reducible. However, after gzipping, we're really only looking at a savings in the tens of KB's (in my case, around a 12kb difference after gzipping). Again, that's without even implementing locale support. Presumably the size difference would shrink after that.

So, in conclusion, I'm not really sure it's worth it to switch over—at least not for size savings. There's many other reasons why using a more functional approach like date-fns (and native Date objects) may be beneficial.

FWIW this module from the people @ smallwins could be a good candidate for a lighter-weight date/time manipulation lib, released pretty recently. It's 35KB total (12KB gzippd).

https://github.com/smallwins/spacetime

This is very important IMO. A gzipped moment bundle is 64.8K which is quite clunky...

@jxom if you're interested, I do have a public fork of react-dates that uses date-fns, with the caveats I mentioned above. It's a few months out of date but I could update it if you're interested.

Hi guys,

I've also added support of date-fns(with locales) in my fork glintik/react-dates@831870db54582304c5667358140f57084e7676d4. Minified build size with date-fns reduced by ~100K. Worth to note, that here is another way to reduce build size with the moment by cutting locales - just add plugin to webpack:

new webpack.IgnorePlugin(/^\.\/locale$/, /moment$/)

We can add support to react-dates of two dates library - moment and date-fns. It provides flexibility and great choice.

Hi @glintik, thanks for the link to your fork! I'm curious as to if you had any idea for how to support both libraries (and if there is a clean/easy way to do that).

Hi @majapw, as a possible solution: introduce a property moment to *Picker components and use it futher. I made a moment-like interface object DateObj that provides moment-methods and uses date-fns inside. So user have a choice, what supply to moment property.
Moment will be removed from dependencies in package.json.

@glintik that seems like it would be a burden on users; they'd have to make react-dates stateful (which it currently isn't) and inject moment, where currently they don't have to do that at all - only moment is stateful (which it already is).

With the release of Luxon, perhaps that can be a viable candidate? Or consider a date formatting interface like @majapw's suggests (https://github.com/airbnb/react-dates/issues/839#issuecomment-347669177)?

Luxon does look more viable to me than date-fns at this time.

Localizations and time zones are implemented by the native Intl API (or a polyfill of it), instead of by the library itself.

This seems like much better solution for covering the localization compared to date-fns.

Here are their docs on output formatting: https://moment.github.io/luxon/docs/manual/formatting.html and here on differences between Luxon and Moment: https://moment.github.io/luxon/docs/manual/why.html

Another thing to consider is that a LOT of people (look at the stars on luxon repo) are moving to luxon from monent for reasons such as immutability and reduced bundle size. However we are then stuck having to use both because of the dependency of this library.
Just a thought to consider. I agree it would be the most viable alternative candidate though, and the immutability may help remove some small bugs too, who knows (unless this uses moment clone which it probably does)? I know I had bugs in my code caused by moment's mutability.

Any movement on this? I recently switched to Luxon from Moment in my projects and it is working very well.

I'm curious about this too. We use react-dates in a project, and the moment payload is massive. It makes no sense to bundle all locales for the client, when the client only needs 1 locale at a time.

There’s no reason you have to bundle all locales; that’s just webpack’s default behavior.

@KingScooty Funny enough, webpack has documentation to exclude the locales from the bundle.

https://webpack.js.org/plugins/ignore-plugin/#ignore-moment-locales

@Magellol But i don't want to "ignore" them. I still want them, just loaded on demand if _and when_ that locale is required.

@KingScooty Not sure if it's my place to say this, but I feel that's somewhat beyond the scope of react-dates.

As already mentioned, It'd be amazing if there could be a way to override the functions react-dates internally uses.
Providing them via props or context could be a solution, in the same fashion https://github.com/dmtrKovalenko/material-ui-pickers allows you to. Needless to say, the interface should be well documented and explained.

date-fns being just functions and strings means you can’t do much validation - ie, i can ensure you’ve given me a moment object, but not a “date function”.

I think it’d be better to wait for the Temporal proposal at this point, and then use a polyfill for it outside of libs like react-dates - that way the bundle size impact would eventually be zero, we’d get first-class primitives with which we could provide precise validation, and we wouldn’t have to endure two very difficult breaking changes for what ends up being a relatively small bundle size gain.

@ljharb I don't understand "you can’t do much validation" date-fns returns Date objects so instanceof Date should be enough to validate that we have a date. am I missing something ?

As for the Temporal proposal, it seems that we'll need for a few years before they reach anything on that.

@onigoetz yes, you’re missing that you can’t validate a function (using propTypes) without calling it, which doesn’t necesssrily happen in the render path.

Oh sorry, I misunderstood the previous comment, that you should be able to pass functions to override the way dates are handled.

Though I agree with you, I don't think the move to date-fns would be a huge improvement in size, what about using the Date class, and relying on Intl, with a polyfill for older browsers ?

That's a lot of extra plumbing, and Intl is a large polyfill, but it'd be an option. I still think that there's no value in making a change right now, until Temporal is available.

Is there any update on this? Grepping through the code, react-dates seem to be using formatting features, firstDayOfWeek(), subtract(n, 'days') in the source code, with more advanced features of moment being used in tests.

It doesn't seem sensible to force a dependency like momentjs, if you're using so little of it? I think it would be better to inline an implementation of subtract(n, 'days') and keep using momentjs in tests, for convenience.

I think you’re severely underestimating how much goes into “formatting”, including locale, language, first day of week, daylight savings time, month boundaries, leap year math, etc.

Fair enough, I assumed Intl was in a state where it could handle these things, but it is not.

This is one of the most well-developed date pickers I've seen, I'm impressed with the accessibility features of it, though the size moment adds is concerning. We are currently using https://github.com/Hacker0x01/react-datepicker which just recently moved away from using moment and towards using the native JavaScript Date object paired with date-fns.

Based on this comment from @ljharb

When it supports every locale that moment does, and passes all of moments test cases, then it is worth having this conversation again. :-)

It looks like moment has support for 123 locales while date-fns has support for 27 locales.

Based on this metric, I don't see date-fns become a viable solution, though I think it's still worth consideration.

Reading through the last meeting notes on Temporal proposal, it sounds like BigInt may also be needed with using Temporal which would need to be polyfilled.

Edit: Maybe only the Temporal polyfill is needed, haven't looked into it too much.

Looks like the temporal pollyfill isn't too big. https://bundlephobia.com/result?p=@wealthbar/tc39[email protected]

I understand the concern of not wanting to make a 2 breaking changes, one to move away from moment to (date-fns, luxon, etc) and one to move to the Temporal proposal.

Slight update: date-fns now support 70 locales: https://github.com/date-fns/date-fns/blob/master/src/locale/index.js

Recent posting from Moment, describing the state of the proejct:

Recently, Chrome Dev Tools started showing recommendations for replacing Moment for the size alone. We generally support this move.
[snip: list of alternatives]
We now generally consider Moment to be a legacy project in maintenance mode. It is not dead, but it is indeed done.

https://momentjs.com/docs/#/-project-status/

I think a lot of people would really love & enjoy a switch to date-fns or #1947 or proposal-temporal. Moment.js indicating that they are a done project & talking about their large size, agreeing with the recommendation to not use Moment for projects, is something I thought worth mentioning in this ticket.

Temporal is not yet stage 3, and thus is much too premature to use.

However, the champions’ current plan is to aim for stage 3 in November, and it’s simply not worth migrating to another date library twice.

Just want to note that adding "driver" support explicitly means that we _dont_ have to migrate date libraries twice.

You migrate once and add driver support. Extensibility and interfaces, similar to other well designed systems.

@tonyhb that is only true if every relevant date library ends up having the same props patterns/design. moment only has one "kind" of object - Temporal has like 6 or more, and it's highly likely that components will need breaking changes to be able to accept only the relevant Temporal types.

No no. We design the interface that this date picker uses (actually done), and we build an adapter over the specific implementations so they conform to our interface (also done for moment and luxon).

Edit: To expand, that's kind of the point of a driver. Build an adapter over your dependencies so that they conform to what _you_ need, and normalize the API that you use so that you can swap out dependencies at will. You then don't care about how the implementation works - they can have the craziest APIs they want! The driver normalizes it.

@tonyhb that is certainly a good goal to strive for, and certainly i understand the point of an adapter :-) what i'm saying is, unless you're designing the interface for Temporal (which is unknown until stage 3), we can't know in advance what the adapter would need to provide to be able to abstract things away sufficiently to make swapping nonbreaking.

I'll drop this discussion after one more comment:

we can't know in advance what the adapter would need to provide to be able to abstract things away sufficiently

Yes, we totally can. I don't understand how you say that. The datepicker already exists! We know what APIs we need to provide to make the date picker work. There are only several functions that we need in a driver for this library - already defined https://github.com/airbnb/react-dates/pull/1947/files#diff-5843b3fcb06308c995f15755473ca448R75-R106. Lets walk through it:

  1. This is the driver functionality that is used in the datepicker. We design it, because it's only used in the datepicker.
  2. There are individual wrappers over each implementation
  3. These wrappers use whatever API the implementation provides under the hood
  4. These wrappers - adapters in common design language - are swappable _at any time_ and _anyone_ can write new adapters at any point in time when future technology changes. Such as temporal, in the future.

We are not designing the interface for temporal! I can't emphasise this any clearer. We are designing the interface for the datepicker. However temporal ends up, we build a wrapper over it. And of course the adapter can't be built until temporal exists. I'm not saying we need a temporal adapter now... we need it after it's ready.

So, no, we can do this now, in advance. Frankly, it's already done. It needs some testing and some actual constructive feedback rather than stonewalling.

Also, pretending the above didn't matter for just a second, it has been said in this thread - for 2 years - "temporal is close". If it's so far away that we don't know the API, please stop saying it's close!

With the statement from Moment.js in mind, could we get a similar statement somewhere on the react-dates README, documenting the intention to keep using moment as-is for now, and eventually replace it with Temporal / the (polyfilled) standard library / whichever other approach?

Personally I’m a happy Moment user, and also very happy they’ve officially clarified the situation that project is in – would be good for react-dates to have a clear position too, considering it’s likely lots of react-dates users are only using Moment because of it.

The lack of decisions here after 2 years makes me afraid of using this library. I'm moving on to a driver based one like react-datepicker too.

@bsides there is no lack of decisions. The decision remains the same as it has always been: moment.js, only, until Temporal is stage 3, at which point we will move to Temporal, only (as should everything else in the ecosystem). There's no value in migrating to date-fns now only to migrate again to Temporal.

This is what moment team is saying - "The Moment team has discussed these issues at length. We recognize that many existing projects may continue to use Moment, but we would like to discourage Moment from being used in new projects going forward. Instead, we would like to recommend alternatives that are excellent choices for use in modern applications today. "

Furthermore it is important now to have date-fns support or general JS Date support.

This isn’t a new project.

i don’t agree it’s important - until Temporal, moment is the de facto standard, no matter what the moment team says; once Temporal arrives, everything else, including moment, date-fns, and Date itself, will be obsolete and should be aggressively migrated away from.

Indeed it is, as of today.

However, a) there’s still some format questions to work out, so browsers are going to ship it behind a flag, and b) a production-ready polyfill doesn’t yet exist.

I’ll be publishing one in the next month or so, at which point we’ll begin migrating.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

swaritkohli picture swaritkohli  Â·  3Comments

Jesus-Gonzalez picture Jesus-Gonzalez  Â·  3Comments

thomasdtucker picture thomasdtucker  Â·  3Comments

augnustin picture augnustin  Â·  3Comments

arthurvi picture arthurvi  Â·  3Comments