---
title: "How do I use a translated string in an input placeholder with react-intl?"
description: "Use the imperative API. `FormattedMessage` renders a React element, but `placeholder`, `title`, `alt` and `aria-label` need plain strings, so call `useIntl()` and pass `intl.formatMessage({ id, defaultMessage })` to the attribute."
category: react
date: 2026-10-09
canonical: https://localizely.com/i18n-questions/react/how-do-i-use-a-translated-string-in-an-input-placeholder-with-react-intl/
---

# How do I use a translated string in an input placeholder with react-intl?

`<FormattedMessage>` is a component; it renders the formatted text into a `React.Fragment` or the element named in `tagName`, so it cannot be assigned to an attribute. `placeholder`, `title`, `alt` and `aria-label` take plain strings, and the react-intl docs list exactly this case, "setting text attributes such as `title`, `aria-label`", as the reason the [imperative API](https://formatjs.github.io/docs/react-intl/api/) exists.

Get the `intl` object with the `useIntl` hook and call `formatMessage`:

```jsx
import { defineMessages, useIntl } from 'react-intl';

const messages = defineMessages({
  searchPlaceholder: {
    id: 'search.placeholder',
    defaultMessage: 'Search',
    description: 'Placeholder text of the search box',
  },
});

function SearchBox() {
  const intl = useIntl();
  const label = intl.formatMessage(messages.searchPlaceholder);

  return <input type="search" placeholder={label} aria-label={label} />;
}
```

An inline descriptor works the same: `intl.formatMessage({ id: 'search.placeholder', defaultMessage: 'Search' })`. `defineMessages` returns the descriptors unchanged; its job is to keep them in a shape the formatjs CLI and Babel plugin can extract, so prefer it over building ids at runtime.

`formatMessage` returns a string as long as every value is a primitive, as in `{ count }` or `{ name: 'Ana' }`. Pass a React element or a rich-text function and you get an array of nodes instead, which is again unusable in an attribute. Variables in attribute text otherwise work like anywhere else; see [How to use dynamic strings in a localization message in React?](/i18n-questions/react/how-to-use-dynamic-strings-in-a-localization-message-in-react/).

`useIntl` only works in function components. The `injectIntl` higher-order component that used to supply `this.props.intl` to class components was removed in react-intl 10 ([upgrade guide](https://formatjs.github.io/docs/react-intl/upgrade-guide-10.x/)). A class you cannot convert can read the exported `IntlContext` through [`static contextType`](https://react.dev/reference/react/Component#static-contexttype) and call `this.context.formatMessage(...)`, or be wrapped by a small function component that calls `useIntl` and passes `intl` down as a prop.

Outside React, for example in a validation helper, build the same object with `createIntl`, as shown in [How to use localization messages in non-component code in React?](/i18n-questions/react/how-to-use-localization-messages-in-non-component-code-in-react/).

With react-i18next there is nothing to work around: `t` returns a string, so `placeholder={t('search.placeholder')}` is all it takes.

Source: https://localizely.com/i18n-questions/react/how-do-i-use-a-translated-string-in-an-input-placeholder-with-react-intl/
