Skip to content

IntlFormatter

Experimental Alpha

This library is a work-in-progress. We are releasing it early to gather feedback, but it is not ready for production.

A comprehensive, framework-agnostic web component that wraps the browser's native Intl.DateTimeFormat and Intl.NumberFormat APIs for formatting dates, numbers, currencies, and percentages with full localization support.

Examples

Vue
Lit
React
Live Preview

Date Formatting

Default:
Full Date with Short Time:
Custom Format:
24-Hour Format:

Date Localization

English (US):
French:
German:
Japanese:

Number Formatting

Default:
No Grouping:
Fixed Decimals (2):

Number Localization

English (US):
French (space separator, comma decimal):
German (dot separator, comma decimal):
Indian (lakh grouping):

Currency Formatting

USD (symbol):
EUR (symbol):
Currency Code Display:
Currency Name Display:

Currency Localization

USD in US:
EUR in Germany:
GBP in UK:
JPY in Japan (no decimals):

Percentage Formatting

Basic:
With Decimals:

Real-World Example: Dashboard Stats

Total Revenue
Total Users
Conversion Rate

CSS Parts Customization

Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.

Styled Currency:
Styled Number:
Styled Date:
Styled Percent:
View Vue Code
<template>
  <section>
    <!-- Date Formatting -->
    <div class="mbe2">
      <h2>Date Formatting</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>Default: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          lang="en-US"
        />
      </div>
      <div>
        <strong>Full Date with Short Time: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="full"
          timeStyle="short"
          lang="en-US"
        />
      </div>
      <div>
        <strong>Custom Format: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          weekday="long"
          year="numeric"
          month="long"
          day="numeric"
          lang="en-US"
        />
      </div>
      <div>
        <strong>24-Hour Format: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          hour="2-digit"
          minute="2-digit"
          second="2-digit"
          hourFormat="24"
          lang="en-US"
        />
      </div>
    </div>

    <!-- Date Localization -->
    <div class="mbe2">
      <h2>Date Localization</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>English (US): </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="full"
          timeStyle="short"
          lang="en-US"
        />
      </div>
      <div>
        <strong>French: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="full"
          timeStyle="short"
          lang="fr-FR"
        />
      </div>
      <div>
        <strong>German: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="full"
          timeStyle="short"
          lang="de-DE"
        />
      </div>
      <div>
        <strong>Japanese: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="full"
          timeStyle="short"
          lang="ja-JP"
        />
      </div>
    </div>

    <!-- Number Formatting -->
    <div class="mbe2">
      <h2>Number Formatting</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>Default: </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="en-US"
        />
      </div>
      <div>
        <strong>No Grouping: </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          :noGrouping="true"
          lang="en-US"
        />
      </div>
      <div>
        <strong>Fixed Decimals (2): </strong>
        <VueIntlFormatter
          type="number"
          :value="100"
          :minimumFractionDigits="2"
          :maximumFractionDigits="2"
          lang="en-US"
        />
      </div>
    </div>

    <!-- Number Localization -->
    <div class="mbe2">
      <h2>Number Localization</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>English (US): </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="en-US"
        />
      </div>
      <div>
        <strong>French (space separator, comma decimal): </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="fr-FR"
        />
      </div>
      <div>
        <strong>German (dot separator, comma decimal): </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="de-DE"
        />
      </div>
      <div>
        <strong>Indian (lakh grouping): </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="hi-IN"
        />
      </div>
    </div>

    <!-- Currency Formatting -->
    <div class="mbe2">
      <h2>Currency Formatting</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>USD (symbol): </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="USD"
          lang="en-US"
        />
      </div>
      <div>
        <strong>EUR (symbol): </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="EUR"
          lang="en-US"
        />
      </div>
      <div>
        <strong>Currency Code Display: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="USD"
          currencyDisplay="code"
          lang="en-US"
        />
      </div>
      <div>
        <strong>Currency Name Display: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="USD"
          currencyDisplay="name"
          lang="en-US"
        />
      </div>
    </div>

    <!-- Currency Localization -->
    <div class="mbe2">
      <h2>Currency Localization</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>USD in US: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="USD"
          lang="en-US"
        />
      </div>
      <div>
        <strong>EUR in Germany: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="EUR"
          lang="de-DE"
        />
      </div>
      <div>
        <strong>GBP in UK: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="GBP"
          lang="en-GB"
        />
      </div>
      <div>
        <strong>JPY in Japan (no decimals): </strong>
        <VueIntlFormatter
          type="currency"
          :value="123456"
          currency="JPY"
          lang="ja-JP"
        />
      </div>
    </div>

    <!-- Percentage Formatting -->
    <div class="mbe2">
      <h2>Percentage Formatting</h2>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>Basic: </strong>
        <VueIntlFormatter
          type="percent"
          :value="0.75"
          lang="en-US"
        />
      </div>
      <div>
        <strong>With Decimals: </strong>
        <VueIntlFormatter
          type="percent"
          :value="0.8532"
          :minimumFractionDigits="2"
          :maximumFractionDigits="2"
          lang="en-US"
        />
      </div>
    </div>

    <!-- Real-World Example: Dashboard Stats -->
    <div class="mbe2">
      <h2>Real-World Example: Dashboard Stats</h2>
    </div>
    <div
      style="
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
        gap: 1rem;
        max-width: 900px;
      "
      class="mbe4"
    >
      <div class="stat-card">
        <div class="stat-label">Total Revenue</div>
        <div class="stat-value">
          <VueIntlFormatter
            type="currency"
            :value="1234567.89"
            currency="USD"
            lang="en-US"
          />
        </div>
        <div class="stat-change positive">
          ↑
          <VueIntlFormatter
            type="percent"
            :value="0.1234"
            :minimumFractionDigits="2"
            lang="en-US"
          />
        </div>
      </div>

      <div class="stat-card">
        <div class="stat-label">Total Users</div>
        <div class="stat-value">
          <VueIntlFormatter
            type="number"
            :value="45678"
            lang="en-US"
          />
        </div>
        <div class="stat-change positive">
          ↑
          <VueIntlFormatter
            type="percent"
            :value="0.0856"
            :minimumFractionDigits="2"
            lang="en-US"
          />
        </div>
      </div>

      <div class="stat-card">
        <div class="stat-label">Conversion Rate</div>
        <div class="stat-value">
          <VueIntlFormatter
            type="percent"
            :value="0.0342"
            :minimumFractionDigits="2"
            lang="en-US"
          />
        </div>
        <div class="stat-change positive">
          ↑
          <VueIntlFormatter
            type="percent"
            :value="0.0523"
            :minimumFractionDigits="2"
            lang="en-US"
          />
        </div>
      </div>
    </div>

    <!-- CSS Parts Customization -->
    <div class="mbe2">
      <h2>CSS Parts Customization</h2>
      <p
        class="mbe2"
        style="color: var(--ag-text-secondary); font-size: 0.875rem;"
      >
        Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
      </p>
    </div>
    <div
      style="display: flex; flex-direction: column; gap: 1rem;"
      class="mbe4"
    >
      <div>
        <strong>Styled Currency: </strong>
        <VueIntlFormatter
          type="currency"
          :value="1234.56"
          currency="USD"
          lang="en-US"
          class="custom-currency"
        />
      </div>
      <div>
        <strong>Styled Number: </strong>
        <VueIntlFormatter
          type="number"
          :value="1234567.89"
          lang="en-US"
          class="custom-number"
        />
      </div>
      <div>
        <strong>Styled Date: </strong>
        <VueIntlFormatter
          type="date"
          date="2024-01-15T14:30:00"
          dateStyle="long"
          timeStyle="short"
          lang="en-US"
          class="custom-date"
        />
      </div>
      <div>
        <strong>Styled Percent: </strong>
        <VueIntlFormatter
          type="percent"
          :value="0.75"
          lang="en-US"
          class="custom-percent"
        />
      </div>
    </div>
  </section>
</template>

<script>
import { VueIntlFormatter } from "agnosticui-core/intl-formatter/vue";

export default {
  name: "IntlFormatterExamples",
  components: {
    VueIntlFormatter,
  },
};
</script>

<style scoped>
/* Dashboard stats styling */
.stat-card {
  background: var(--ag-background);
  border: 1px solid var(--ag-border);
  border-radius: var(--ag-radius-md);
  padding: 1.5rem;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.stat-label {
  font-size: 0.875rem;
  color: var(--ag-text-secondary);
  margin-bottom: 0.5rem;
}

.stat-value {
  font-size: clamp(1.25rem, 4vw, 1.875rem);
  font-weight: 700;
  color: var(--ag-text);
  overflow-wrap: break-word;
  word-break: break-word;
  line-height: 1.2;
}

.stat-change {
  font-size: 0.875rem;
  margin-top: 0.5rem;
}

.stat-change.positive {
  color: var(--ag-success-text);
}

/* CSS Parts customization examples */

/* Custom currency style */
.custom-currency::part(currency) {
  color: var(--ag-success-text);
  font-weight: bold;
  font-size: 1.25rem;
}

/* Custom number style */
.custom-number::part(number) {
  font-family: "Courier New", monospace;
  background: var(--ag-background-subtle);
  padding: 0.25rem 0.5rem;
  border-radius: var(--ag-radius-sm);
}

/* Custom date style */
.custom-date::part(date-time) {
  color: var(--ag-primary-text);
  font-style: italic;
}

/* Custom percent style */
.custom-percent::part(percent) {
  color: var(--ag-danger-text);
  font-weight: 600;
}
</style>
Live Preview
View Lit / Web Component Code
import { LitElement, html, css } from 'lit';
import 'agnosticui-core/intl-formatter';

export class IntlFormatterLitExamples extends LitElement {
  createRenderRoot() {
    return this;
  }

  static styles = css`
    /* Dashboard stats styling */
    .stat-card {
      background: var(--ag-background);
      border: 1px solid var(--ag-border);
      border-radius: var(--ag-radius-md);
      padding: 1.5rem;
      box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
    }

    .stat-label {
      font-size: 0.875rem;
      color: var(--ag-text-secondary);
      margin-bottom: 0.5rem;
    }

    .stat-value {
      font-size: clamp(1.25rem, 4vw, 1.875rem);
      font-weight: 700;
      color: var(--ag-text);
      overflow-wrap: break-word;
      word-break: break-word;
      line-height: 1.2;
    }

    .stat-change {
      font-size: 0.875rem;
      margin-top: 0.5rem;
    }

    .stat-change.positive {
      color: var(--ag-success-text);
    }

    /* CSS Parts customization examples */

    /* Custom currency style */
    .custom-currency::part(currency) {
      color: var(--ag-success-text);
      font-weight: bold;
      font-size: 1.25rem;
    }

    /* Custom number style */
    .custom-number::part(number) {
      font-family: "Courier New", monospace;
      background: var(--ag-background-subtle);
      padding: 0.25rem 0.5rem;
      border-radius: var(--ag-radius-sm);
    }

    /* Custom date style */
    .custom-date::part(date-time) {
      color: var(--ag-primary-text);
      font-style: italic;
    }

    /* Custom percent style */
    .custom-percent::part(percent) {
      color: var(--ag-danger-text);
      font-weight: 600;
    }
  `;

  render() {
    return html`
      <section>
        <!-- Date Formatting -->
        <div class="mbe2">
          <h2>Date Formatting</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>Default: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Full Date with Short Time: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="full"
              time-style="short"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Custom Format: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              weekday="long"
              year="numeric"
              month="long"
              day="numeric"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>24-Hour Format: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              hour="2-digit"
              minute="2-digit"
              second="2-digit"
              hour-format="24"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Date Localization -->
        <div class="mbe2">
          <h2>Date Localization</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>English (US): </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="full"
              time-style="short"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>French: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="full"
              time-style="short"
              lang="fr-FR"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>German: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="full"
              time-style="short"
              lang="de-DE"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Japanese: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="full"
              time-style="short"
              lang="ja-JP"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Number Formatting -->
        <div class="mbe2">
          <h2>Number Formatting</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>Default: </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>No Grouping: </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              no-grouping
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Fixed Decimals (2): </strong>
            <ag-intl-formatter
              type="number"
              .value=${100}
              .minimumFractionDigits=${2}
              .maximumFractionDigits=${2}
              lang="en-US"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Number Localization -->
        <div class="mbe2">
          <h2>Number Localization</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>English (US): </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>French (space separator, comma decimal): </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="fr-FR"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>German (dot separator, comma decimal): </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="de-DE"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Indian (lakh grouping): </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="hi-IN"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Currency Formatting -->
        <div class="mbe2">
          <h2>Currency Formatting</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>USD (symbol): </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="USD"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>EUR (symbol): </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="EUR"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Currency Code Display: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="USD"
              currency-display="code"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Currency Name Display: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="USD"
              currency-display="name"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Currency Localization -->
        <div class="mbe2">
          <h2>Currency Localization</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>USD in US: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="USD"
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>EUR in Germany: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="EUR"
              lang="de-DE"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>GBP in UK: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="GBP"
              lang="en-GB"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>JPY in Japan (no decimals): </strong>
            <ag-intl-formatter
              type="currency"
              .value=${123456}
              currency="JPY"
              lang="ja-JP"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Percentage Formatting -->
        <div class="mbe2">
          <h2>Percentage Formatting</h2>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>Basic: </strong>
            <ag-intl-formatter
              type="percent"
              .value=${0.75}
              lang="en-US"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>With Decimals: </strong>
            <ag-intl-formatter
              type="percent"
              .value=${0.8532}
              .minimumFractionDigits=${2}
              .maximumFractionDigits=${2}
              lang="en-US"
            ></ag-intl-formatter>
          </div>
        </div>

        <!-- Real-World Example: Dashboard Stats -->
        <div class="mbe2">
          <h2>Real-World Example: Dashboard Stats</h2>
        </div>
        <div
          style="
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
            gap: 1rem;
            max-width: 900px;
          "
          class="mbe4"
        >
          <div class="stat-card">
            <div class="stat-label">Total Revenue</div>
            <div class="stat-value">
              <ag-intl-formatter
                type="currency"
                .value=${1234567.89}
                currency="USD"
                lang="en-US"
              ></ag-intl-formatter>
            </div>
            <div class="stat-change positive">
              ↑
              <ag-intl-formatter
                type="percent"
                .value=${0.1234}
                .minimumFractionDigits=${2}
                lang="en-US"
              ></ag-intl-formatter>
            </div>
          </div>

          <div class="stat-card">
            <div class="stat-label">Total Users</div>
            <div class="stat-value">
              <ag-intl-formatter
                type="number"
                .value=${45678}
                lang="en-US"
              ></ag-intl-formatter>
            </div>
            <div class="stat-change positive">
              ↑
              <ag-intl-formatter
                type="percent"
                .value=${0.0856}
                .minimumFractionDigits=${2}
                lang="en-US"
              ></ag-intl-formatter>
            </div>
          </div>

          <div class="stat-card">
            <div class="stat-label">Conversion Rate</div>
            <div class="stat-value">
              <ag-intl-formatter
                type="percent"
                .value=${0.0342}
                .minimumFractionDigits=${2}
                lang="en-US"
              ></ag-intl-formatter>
            </div>
            <div class="stat-change positive">
              ↑
              <ag-intl-formatter
                type="percent"
                .value=${0.0523}
                .minimumFractionDigits=${2}
                lang="en-US"
              ></ag-intl-formatter>
            </div>
          </div>
        </div>

        <!-- CSS Parts Customization -->
        <div class="mbe2">
          <h2>CSS Parts Customization</h2>
          <p class="mbe2" style="color: var(--ag-text-secondary); font-size: 0.875rem;">
            Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
          </p>
        </div>
        <div style="display: flex; flex-direction: column; gap: 1rem;" class="mbe4">
          <div>
            <strong>Styled Currency: </strong>
            <ag-intl-formatter
              type="currency"
              .value=${1234.56}
              currency="USD"
              lang="en-US"
              class="custom-currency"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Styled Number: </strong>
            <ag-intl-formatter
              type="number"
              .value=${1234567.89}
              lang="en-US"
              class="custom-number"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Styled Date: </strong>
            <ag-intl-formatter
              type="date"
              date="2024-01-15T14:30:00"
              date-style="long"
              time-style="short"
              lang="en-US"
              class="custom-date"
            ></ag-intl-formatter>
          </div>
          <div>
            <strong>Styled Percent: </strong>
            <ag-intl-formatter
              type="percent"
              .value=${0.75}
              lang="en-US"
              class="custom-percent"
            ></ag-intl-formatter>
          </div>
        </div>
      </section>
    `;
  }
}

// Register the custom element
customElements.define('intl-formatter-lit-examples', IntlFormatterLitExamples);

Interactive Preview: Click the "Open in StackBlitz" button below to see this example running live in an interactive playground.

View React Code
import { ReactIntlFormatter } from "agnosticui-core/intl-formatter/react";

export default function IntlFormatterReactExamples() {
  return (
    <section>
      <style>{`
        /* Dashboard stats styling */
        .stat-card {
          background: var(--ag-background);
          border: 1px solid var(--ag-border);
          border-radius: var(--ag-radius-md);
          padding: 1.5rem;
          box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
        }

        .stat-label {
          font-size: 0.875rem;
          color: var(--ag-text-secondary);
          margin-bottom: 0.5rem;
        }

        .stat-value {
          font-size: clamp(1.25rem, 4vw, 1.875rem);
          font-weight: 700;
          color: var(--ag-text);
          overflow-wrap: break-word;
          word-break: break-word;
          line-height: 1.2;
        }

        .stat-change {
          font-size: 0.875rem;
          margin-top: 0.5rem;
        }

        .stat-change.positive {
          color: var(--ag-success-text);
        }

        /* CSS Parts customization examples */

        /* Custom currency style */
        .custom-currency::part(currency) {
          color: var(--ag-success-text);
          font-weight: bold;
          font-size: 1.25rem;
        }

        /* Custom number style */
        .custom-number::part(number) {
          font-family: "Courier New", monospace;
          background: var(--ag-background-subtle);
          padding: 0.25rem 0.5rem;
          border-radius: var(--ag-radius-sm);
        }

        /* Custom date style */
        .custom-date::part(date-time) {
          color: var(--ag-primary-text);
          font-style: italic;
        }

        /* Custom percent style */
        .custom-percent::part(percent) {
          color: var(--ag-danger-text);
          font-weight: 600;
        }
      `}</style>

      {/* Date Formatting */}
      <div className="mbe2">
        <h2>Date Formatting</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>Default: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            lang="en-US"
          />
        </div>
        <div>
          <strong>Full Date with Short Time: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="full"
            timeStyle="short"
            lang="en-US"
          />
        </div>
        <div>
          <strong>Custom Format: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            weekday="long"
            year="numeric"
            month="long"
            day="numeric"
            lang="en-US"
          />
        </div>
        <div>
          <strong>24-Hour Format: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            hour="2-digit"
            minute="2-digit"
            second="2-digit"
            hourFormat="24"
            lang="en-US"
          />
        </div>
      </div>

      {/* Date Localization */}
      <div className="mbe2">
        <h2>Date Localization</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>English (US): </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="full"
            timeStyle="short"
            lang="en-US"
          />
        </div>
        <div>
          <strong>French: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="full"
            timeStyle="short"
            lang="fr-FR"
          />
        </div>
        <div>
          <strong>German: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="full"
            timeStyle="short"
            lang="de-DE"
          />
        </div>
        <div>
          <strong>Japanese: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="full"
            timeStyle="short"
            lang="ja-JP"
          />
        </div>
      </div>

      {/* Number Formatting */}
      <div className="mbe2">
        <h2>Number Formatting</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>Default: </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="en-US"
          />
        </div>
        <div>
          <strong>No Grouping: </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            noGrouping={true}
            lang="en-US"
          />
        </div>
        <div>
          <strong>Fixed Decimals (2): </strong>
          <ReactIntlFormatter
            type="number"
            value={100}
            minimumFractionDigits={2}
            maximumFractionDigits={2}
            lang="en-US"
          />
        </div>
      </div>

      {/* Number Localization */}
      <div className="mbe2">
        <h2>Number Localization</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>English (US): </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="en-US"
          />
        </div>
        <div>
          <strong>French (space separator, comma decimal): </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="fr-FR"
          />
        </div>
        <div>
          <strong>German (dot separator, comma decimal): </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="de-DE"
          />
        </div>
        <div>
          <strong>Indian (lakh grouping): </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="hi-IN"
          />
        </div>
      </div>

      {/* Currency Formatting */}
      <div className="mbe2">
        <h2>Currency Formatting</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>USD (symbol): </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="USD"
            lang="en-US"
          />
        </div>
        <div>
          <strong>EUR (symbol): </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="EUR"
            lang="en-US"
          />
        </div>
        <div>
          <strong>Currency Code Display: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="USD"
            currencyDisplay="code"
            lang="en-US"
          />
        </div>
        <div>
          <strong>Currency Name Display: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="USD"
            currencyDisplay="name"
            lang="en-US"
          />
        </div>
      </div>

      {/* Currency Localization */}
      <div className="mbe2">
        <h2>Currency Localization</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>USD in US: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="USD"
            lang="en-US"
          />
        </div>
        <div>
          <strong>EUR in Germany: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="EUR"
            lang="de-DE"
          />
        </div>
        <div>
          <strong>GBP in UK: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="GBP"
            lang="en-GB"
          />
        </div>
        <div>
          <strong>JPY in Japan (no decimals): </strong>
          <ReactIntlFormatter
            type="currency"
            value={123456}
            currency="JPY"
            lang="ja-JP"
          />
        </div>
      </div>

      {/* Percentage Formatting */}
      <div className="mbe2">
        <h2>Percentage Formatting</h2>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>Basic: </strong>
          <ReactIntlFormatter
            type="percent"
            value={0.75}
            lang="en-US"
          />
        </div>
        <div>
          <strong>With Decimals: </strong>
          <ReactIntlFormatter
            type="percent"
            value={0.8532}
            minimumFractionDigits={2}
            maximumFractionDigits={2}
            lang="en-US"
          />
        </div>
      </div>

      {/* Real-World Example: Dashboard Stats */}
      <div className="mbe2">
        <h2>Real-World Example: Dashboard Stats</h2>
      </div>
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(240px, 1fr))",
          gap: "1rem",
          maxWidth: "900px",
        }}
        className="mbe4"
      >
        <div className="stat-card">
          <div className="stat-label">Total Revenue</div>
          <div className="stat-value">
            <ReactIntlFormatter
              type="currency"
              value={1234567.89}
              currency="USD"
              lang="en-US"
            />
          </div>
          <div className="stat-change positive">
            ↑{" "}
            <ReactIntlFormatter
              type="percent"
              value={0.1234}
              minimumFractionDigits={2}
              lang="en-US"
            />
          </div>
        </div>

        <div className="stat-card">
          <div className="stat-label">Total Users</div>
          <div className="stat-value">
            <ReactIntlFormatter
              type="number"
              value={45678}
              lang="en-US"
            />
          </div>
          <div className="stat-change positive">
            ↑{" "}
            <ReactIntlFormatter
              type="percent"
              value={0.0856}
              minimumFractionDigits={2}
              lang="en-US"
            />
          </div>
        </div>

        <div className="stat-card">
          <div className="stat-label">Conversion Rate</div>
          <div className="stat-value">
            <ReactIntlFormatter
              type="percent"
              value={0.0342}
              minimumFractionDigits={2}
              lang="en-US"
            />
          </div>
          <div className="stat-change positive">
            ↑{" "}
            <ReactIntlFormatter
              type="percent"
              value={0.0523}
              minimumFractionDigits={2}
              lang="en-US"
            />
          </div>
        </div>
      </div>

      {/* CSS Parts Customization */}
      <div className="mbe2">
        <h2>CSS Parts Customization</h2>
        <p className="mbe2" style={{ color: "var(--ag-text-secondary)", fontSize: "0.875rem" }}>
          Customize formatter appearance using CSS Shadow Parts without breaking encapsulation.
        </p>
      </div>
      <div style={{ display: "flex", flexDirection: "column", gap: "1rem" }} className="mbe4">
        <div>
          <strong>Styled Currency: </strong>
          <ReactIntlFormatter
            type="currency"
            value={1234.56}
            currency="USD"
            lang="en-US"
            className="custom-currency"
          />
        </div>
        <div>
          <strong>Styled Number: </strong>
          <ReactIntlFormatter
            type="number"
            value={1234567.89}
            lang="en-US"
            className="custom-number"
          />
        </div>
        <div>
          <strong>Styled Date: </strong>
          <ReactIntlFormatter
            type="date"
            date="2024-01-15T14:30:00"
            dateStyle="long"
            timeStyle="short"
            lang="en-US"
            className="custom-date"
          />
        </div>
        <div>
          <strong>Styled Percent: </strong>
          <ReactIntlFormatter
            type="percent"
            value={0.75}
            lang="en-US"
            className="custom-percent"
          />
        </div>
      </div>
    </section>
  );
}
Open in StackBlitz

Features

  • Full Internationalization - Uses browser's native Intl API
  • Performance Optimized - Built-in formatter caching
  • Type Safe - Complete TypeScript definitions
  • Styleable - CSS parts for custom styling
  • Error Handling - Custom events for validation errors
  • Framework Support - React and Vue wrappers included
  • Accessible - Semantic HTML with proper attributes
  • Zero Dependencies - Built on Lit Element

Usage

TIP

The framework examples below import AgnosticUI as an npm package. Alternatively, you can use the CLI for complete control, AI/LLM visibility, and full code ownership:

bash
npx ag init --framework FRAMEWORK # react, vue, lit, svelte, etc.
npx ag add IntlFormatter

The CLI copies source code directly into your project, giving you full visibility and control. After running npx ag add, you'll receive exact import instructions.

Vue
vue
<template>
  <section>
    <VueIntlFormatter
      type="date"
      date="2024-01-15"
      dateStyle="long"
      lang="en-US"
    />

    <VueIntlFormatter
      type="currency"
      :value="1234.56"
      currency="USD"
      lang="en-US"
    />

    <VueIntlFormatter
      type="number"
      :value="1234567.89"
      :minimumFractionDigits="2"
      lang="en-US"
    />

    <VueIntlFormatter
      type="percent"
      :value="0.75"
      lang="en-US"
    />
  </section>
</template>

<script setup>
import { VueIntlFormatter } from "agnosticui-core/intl-formatter/vue";
</script>
React
tsx
import { ReactIntlFormatter } from "agnosticui-core/intl-formatter/react";

function App() {
  const handleError = (event) => {
    console.error('Formatting error:', event.detail);
  };

  return (
    <div>
      <ReactIntlFormatter
        type="date"
        date="2024-01-15"
        dateStyle="full"
        timeStyle="short"
      />

      <ReactIntlFormatter
        type="currency"
        value={1234.56}
        currency="EUR"
        lang="de-DE"
        onFormatError={handleError}
      />

      <ReactIntlFormatter
        type="number"
        value={1234567.89}
        minimumFractionDigits={2}
        maximumFractionDigits={2}
        noGrouping={false}
      />

      <ReactIntlFormatter
        type="percent"
        value={0.8532}
        minimumFractionDigits={2}
      />
    </div>
  );
}
Vanilla HTML/JavaScript
html
<script type="module">
  import "agnosticui-core/intl-formatter";
</script>

<ag-intl-formatter 
  type="date" 
  date="2024-01-15"
  date-style="long"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="currency" 
  value="1234.56"
  currency="USD"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="number" 
  value="1234567.89"
  minimum-fraction-digits="2"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter
  type="percent"
  value="0.75"
  lang="en-US">
</ag-intl-formatter>

API Reference

Common Properties

PropertyTypeDefaultDescription
type'date' | 'number' | 'percent' | 'currency''date'The type of formatting to apply
valuenumber | string | Date-The value to format (for numbers/currency/percent)
langstringBrowser localeThe locale to use (e.g., 'en-US', 'fr-FR')

Date Properties

PropertyTypeDefaultDescription
dateDate | string-The date value to format
dateStyle'full' | 'long' | 'medium' | 'short'-Shortcut for date formatting style
timeStyle'full' | 'long' | 'medium' | 'short'-Shortcut for time formatting style
weekday'narrow' | 'short' | 'long'-Weekday representation
era'narrow' | 'short' | 'long'-Era representation (e.g., 'AD')
year'numeric' | '2-digit'-Year representation
month'numeric' | '2-digit' | 'narrow' | 'short' | 'long'-Month representation
day'numeric' | '2-digit'-Day representation
hour'numeric' | '2-digit'-Hour representation
minute'numeric' | '2-digit'-Minute representation
second'numeric' | '2-digit'-Second representation
timeZoneName'short' | 'long'-Time zone name representation
timeZonestring-IANA time zone identifier
hourFormat'auto' | '12' | '24''auto'Hour format preference

Number Properties

PropertyTypeDefaultDescription
noGroupingbooleanfalseDisable grouping separators (e.g., 1000 vs 1,000)
minimumIntegerDigitsnumber-Minimum number of integer digits
minimumFractionDigitsnumber-Minimum number of fraction digits
maximumFractionDigitsnumber-Maximum number of fraction digits
minimumSignificantDigitsnumber-Minimum number of significant digits
maximumSignificantDigitsnumber-Maximum number of significant digits

Currency Properties

PropertyTypeDefaultDescription
currencystring'USD'ISO 4217 currency code (e.g., 'USD', 'EUR')
currencyDisplay'symbol' | 'narrowSymbol' | 'code' | 'name''symbol'How to display the currency

Events

format-error

Dispatched when formatting fails due to validation or runtime errors.

typescript
interface FormatErrorDetail {
  type: 'date' | 'number';
  error: string;
}

Example:

javascript
element.addEventListener("format-error", (event) => {
  console.error(`${event.detail.type} formatting error:`, event.detail.error);
});

CSS Parts

Style the component using CSS ::part() selectors:

css
ag-intl-formatter::part(date-time) {
  color: blue;
  font-weight: bold;
}

ag-intl-formatter::part(number) {
  font-family: monospace;
}

ag-intl-formatter::part(currency) {
  color: green;
}

ag-intl-formatter::part(percent) {
  font-weight: bold;
}

ag-intl-formatter::part(error) {
  color: red;
}

Examples

Date Formatting

html
<ag-intl-formatter 
  type="date"
  date="2024-01-15T14:30:00"
  date-style="full"
  time-style="short"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="date"
  date="2024-01-15"
  weekday="long"
  year="numeric"
  month="long"
  day="numeric"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="date"
  date="2024-01-15T14:30:00"
  hour="2-digit"
  minute="2-digit"
  hour-format="24"
  lang="en-US">
</ag-intl-formatter>

Number Formatting

html
<ag-intl-formatter 
  type="number"
  value="1234567.89"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="number"
  value="1234567.89"
  no-grouping
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="number"
  value="100"
  minimum-fraction-digits="2"
  maximum-fraction-digits="2"
  lang="en-US">
</ag-intl-formatter>

Currency Formatting

html
<ag-intl-formatter 
  type="currency"
  value="1234.56"
  currency="USD"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="currency"
  value="1234.56"
  currency="EUR"
  currency-display="code"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="currency"
  value="1234"
  currency="JPY"
  lang="ja-JP">
</ag-intl-formatter>

Percentage Formatting

html
<ag-intl-formatter 
  type="percent"
  value="0.75"
  lang="en-US">
</ag-intl-formatter>

<ag-intl-formatter 
  type="percent"
  value="0.8532"
  minimum-fraction-digits="2"
  maximum-fraction-digits="2"
  lang="en-US">
</ag-intl-formatter>

Localization Examples

html
<ag-intl-formatter 
  type="number"
  value="1234.56"
  lang="fr-FR">
</ag-intl-formatter>

<ag-intl-formatter 
  type="currency"
  value="1234.56"
  currency="EUR"
  lang="de-DE">
</ag-intl-formatter>

<ag-intl-formatter 
  type="date"
  date="2024-01-15"
  date-style="long"
  lang="ja-JP">
</ag-intl-formatter>

Performance

The component uses intelligent formatter caching to improve performance:

  • Formatters are cached based on type and options
  • Cache is automatically cleared when the component is removed
  • Repeated renders with the same options reuse cached formatters

This makes the component suitable for rendering large lists or frequently updating values.

Browser Support

Works in all modern browsers that support:

  • Web Components (Custom Elements v1)
  • Shadow DOM v1
  • Intl.DateTimeFormat
  • Intl.NumberFormat

For older browsers, consider using polyfills:

TypeScript Support

Full TypeScript definitions are included. Import types as needed:

typescript
import type { IntlFormatterProps } from "agnosticui-core/intl-formatter";

Testing

The component includes a comprehensive test suite with 50+ tests covering:

  • All formatting types
  • Validation and error handling
  • Localization
  • Edge cases
  • Performance
  • Accessibility

Run tests with:

bash
npm test

Contributing

Contributions are welcome! Please ensure:

  • All tests pass
  • New features include tests
  • Code follows the existing style
  • TypeScript types are updated

License

MIT