Skip to main content

Localizing dates in React Native the right way

Localizing dates in React Native the right way

There are a lot of things the world can't seem to agree on, and one of them is how to format dates.

Girl: "What's your idea of a perfect date?"

Engineer: "DD/MM/YYYY. I find other formats a bit confusing."

Dates can be structured in a multitude of ways:

  • DD/MM/YYYY (most of Europe, Australia)
  • MM/DD/YYYY (United States)
  • YYYY/MM/DD (ISO 8601, East Asia)
  • DD.MM.YYYY (Germany, Finland, Russia)
  • YYYY.MM.DD (Hungary, Lithuania)
  • DD-MM-YYYY (Netherlands, South Africa)
  • YYYY-MM-DD (ISO 8601, Sweden, Canada)

What varies is both the separator between the numbers and the order of day, month, and year. In the US, the most common format is MM/DD/YYYY. In most of Europe, it's DD/MM/YYYY or DD.MM.YYYY.

I've built enough UIs over the years to know that eventually you'll need to localize your dates. I remember particularly well a case where we tried to push the European date format to US users. We received a lot of user feedback—zero positive. Turns out Americans don't appreciate you pushing your "superior" date format on them.

Now that we've established the problem, let's look at the solution.

The Challenge with React Native

Date localization in React Native is an interesting topic. If you navigate to Settings on iOS, you'll find a section in General → Language & Region that shows the user's preferred date and time formats. These settings are based on the language and region configured on the device.

The challenge is that React Native doesn't directly expose these system preferences. You can't simply ask the OS "what date format does this user prefer?" Instead, we need to combine a few tools to achieve proper localization.

The Solution: Intl.DateTimeFormat + react-native-localize

The approach combines two things:

  1. Intl.DateTimeFormat — A native JavaScript API for locale-aware date formatting
  2. react-native-localize — A library that exposes the device's locale information

Step 1: Install react-native-localize

First, install the library:

npm install react-native-localize
# or
yarn add react-native-localize

For iOS, run pod install:

cd ios && pod install && cd ..

Step 2: Understanding Intl.DateTimeFormat

Intl.DateTimeFormat is part of the ECMAScript Internationalization API (ECMA-402). It's supported in modern JavaScript engines, including Hermes (React Native's default engine since 0.70).

Here's a basic example:

const date = new Date('2023-10-15')

// US English format
new Intl.DateTimeFormat('en-US').format(date) // "10/15/2023"

// Finnish format
new Intl.DateTimeFormat('fi-FI').format(date) // "15.10.2023"

// German format
new Intl.DateTimeFormat('de-DE').format(date) // "15.10.2023"

// Japanese format
new Intl.DateTimeFormat('ja-JP').format(date) // "2023/10/15"

The beauty of Intl.DateTimeFormat is that it handles all the complexity of different locales for you. You just need to provide the correct locale code.

Step 3: Getting the Device Locale

This is where react-native-localize comes in. It provides the getLocales() function that returns an array of the user's preferred locales:

import { getLocales } from 'react-native-localize'

const locales = getLocales()
console.log(locales)
// [
//   { countryCode: "FI", languageTag: "fi-FI", languageCode: "fi", isRTL: false },
//   { countryCode: "US", languageTag: "en-US", languageCode: "en", isRTL: false }
// ]

The first item in the array is the user's primary locale. We can use the languageTag (e.g., "fi-FI") directly with Intl.DateTimeFormat.

Step 4: Putting It Together

Here's a simple utility function to format dates according to the user's locale:

import { getLocales } from 'react-native-localize'

export const formatLocalizedDate = (date: Date): string => {
  const [{ languageTag }] = getLocales()

  return new Intl.DateTimeFormat(languageTag, {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
  }).format(date)
}

// Usage
const date = new Date('2023-10-15')
formatLocalizedDate(date) // "15.10.2023" for Finnish users, "10/15/2023" for US users

Advanced: Getting the Format Pattern String

Sometimes you need the format pattern itself (like "DD.MM.YYYY") rather than a formatted date. This is useful when you're working with date picker libraries or displaying placeholder text.

The trick is to use formatToParts(), which breaks down the formatted date into its components:

import { getLocales } from 'react-native-localize'

export const getDateFormatString = (): string => {
  const [{ languageTag }] = getLocales()

  return new Intl.DateTimeFormat(languageTag, {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
  })
    .formatToParts(new Date())
    .map((part) => {
      switch (part.type) {
        case 'day':
          return 'DD'
        case 'month':
          return 'MM'
        case 'year':
          return 'YYYY'
        default:
          return part.value // This preserves separators like ".", "/", "-"
      }
    })
    .join('')
}

// Returns "DD.MM.YYYY" for Finnish, "MM/DD/YYYY" for US

Working with date-fns

If you're using date-fns for date manipulation (and you probably should be), you can combine the format string with its format function:

import { format } from 'date-fns'
import { getLocales } from 'react-native-localize'

const getDateFormatString = (): string => {
  const [{ languageTag }] = getLocales()

  return new Intl.DateTimeFormat(languageTag, {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
  })
    .formatToParts(new Date())
    .map((part) => {
      switch (part.type) {
        case 'day':
          return 'dd'
        case 'month':
          return 'MM'
        case 'year':
          return 'yyyy'
        default:
          return part.value
      }
    })
    .join('')
}

export const formatLocalizedDate = (date: Date): string => {
  return format(date, getDateFormatString())
}

Note that date-fns uses lowercase dd for day and yyyy for year, which differs from the common uppercase notation.

Handling Time Formats

The same approach works for time formatting. Users have strong preferences about 12-hour vs 24-hour time formats:

export const formatLocalizedTime = (date: Date): string => {
  const [{ languageTag }] = getLocales()

  return new Intl.DateTimeFormat(languageTag, {
    hour: 'numeric',
    minute: 'numeric',
  }).format(date)
}

// "2:30 PM" for US users, "14:30" for most European users

Complete Utility Module

Here's a complete utility module you can drop into your project:

// utils/dateLocalization.ts
import { getLocales } from 'react-native-localize'

const getLocale = (): string => {
  const [{ languageTag }] = getLocales()
  return languageTag
}

export const formatLocalizedDate = (date: Date): string => {
  return new Intl.DateTimeFormat(getLocale(), {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
  }).format(date)
}

export const formatLocalizedDateTime = (date: Date): string => {
  return new Intl.DateTimeFormat(getLocale(), {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
    hour: 'numeric',
    minute: 'numeric',
  }).format(date)
}

export const formatLocalizedTime = (date: Date): string => {
  return new Intl.DateTimeFormat(getLocale(), {
    hour: 'numeric',
    minute: 'numeric',
  }).format(date)
}

export const formatRelativeDate = (date: Date): string => {
  return new Intl.DateTimeFormat(getLocale(), {
    month: 'short',
    day: 'numeric',
  }).format(date)
}

export const getDateFormatPattern = (): string => {
  return new Intl.DateTimeFormat(getLocale(), {
    day: 'numeric',
    month: 'numeric',
    year: 'numeric',
  })
    .formatToParts(new Date())
    .map((part) => {
      switch (part.type) {
        case 'day':
          return 'DD'
        case 'month':
          return 'MM'
        case 'year':
          return 'YYYY'
        default:
          return part.value
      }
    })
    .join('')
}

Gotchas and Tips

Hermes and Intl Support

If you're using Hermes (the default since React Native 0.70), Intl is fully supported. For older versions or if Hermes is disabled, you might need to polyfill it with packages like intl or @formatjs/intl-datetimeformat.

Caching the Locale

The locale doesn't change during app usage (unless the user changes their device settings and restarts the app). Consider caching the locale value:

let cachedLocale: string | null = null

const getLocale = (): string => {
  if (!cachedLocale) {
    const [{ languageTag }] = getLocales()
    cachedLocale = languageTag
  }
  return cachedLocale
}

Testing Different Locales

During development, you can test different locales by changing the language and region settings on your simulator or device. On iOS Simulator: Settings → General → Language & Region.

Conclusion

Proper date localization isn't just a nice-to-have—it's essential for any app with international users. The combination of Intl.DateTimeFormat and react-native-localize gives you everything you need to display dates in a format your users expect.

The key takeaways:

  1. Never hardcode date formats—use Intl.DateTimeFormat with the user's locale
  2. Use react-native-localize to get the device's locale settings
  3. The formatToParts() method is your friend when you need the format pattern string
  4. The same approach works for time formatting

Your American users will thank you for showing them 10/15/2023 instead of 15.10.2023.

More to read

What I'm reading in 2026

Flights of 2025