diff --git a/README.md b/README.md
index 4685131..bbdddcf 100644
--- a/README.md
+++ b/README.md
@@ -1,148 +1,163 @@
-# RIFM - React Input Format & Mask
+# RIFM — React Input Format & Mask
-Is a tiny (≈ 800b) component (and hook) to transform any input component
-into formatted or masked input.
+A small, dependency-free React hook and component for building formatted and masked inputs without losing the cursor position.
-[Demo](https://trysound.github.io/rifm)
+[Live demo](https://trysound.github.io/rifm)
-## Highlights
+## Why RIFM?
-- Requires React 16.8+
-- Dependency free
-- Tiny (≈ 800b)
-- Supports any [input](https://trysound.github.io/rifm#material-ui).
-- Can [mask](https://trysound.github.io/rifm#date-format) input,
- [format](https://trysound.github.io/rifm#number-format) and [more](https://trysound.github.io/rifm#case-enforcement)
-- Small readable source
-- flow + typescript definitions
+- Keeps the caret in the expected place while formatting
+- Works with native inputs, textareas, and custom input components
+- Supports formatting, masks, case enforcement, and other transformations
+- Provides both a hook and a render-prop component
+- Has no runtime dependencies and is about 1.4 kB gzipped
+- Includes TypeScript declarations
+- Supports React 16.8 and newer
-## Example
+## Install
-Rifm offers both a Render Prop and a Hook API:
+```sh
+pnpm add rifm
+```
-### Render Prop
+You can also install it with `npm install rifm` or `yarn add rifm`.
-```js
-import { Rifm } from 'rifm';
-import { TextField } from '@material-ui/core';
+## Quick start
-const numberFormat = (str: string) => {
- const r = parseInt(str.replace(/[^\d]+/gi, ''), 10);
- return r ? r.toLocaleString('en') : '';
-}
+RIFM is controlled: keep the formatted value in state, pass it to `useRifm`, and attach the returned props to your input.
-...
-
- const [value, setValue] = React.useState('')
-
-
- {({ value, onChange }) => (
-
- )}
-
-
-...
-```
+```tsx
+import { useState } from "react";
+import { useRifm } from "rifm";
-### Hook
+const formatInteger = (value: string) => {
+ const digits = value.replace(/\D/g, "");
+ return digits.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
-```js
-import { useRifm } from 'rifm';
-import { TextField } from '@material-ui/core';
+export function PriceInput() {
+ const [value, setValue] = useState("");
+ const rifm = useRifm({
+ value,
+ onChange: setValue,
+ format: formatInteger,
+ });
-const numberFormat = (str: string) => {
- const r = parseInt(str.replace(/[^\d]+/gi, ''), 10);
- return r ? r.toLocaleString('en') : '';
+ return ;
}
+```
-...
+The `onChange` callback receives the formatted string—not the React change event.
- const [value, setValue] = React.useState('')
+> [!IMPORTANT]
+> Use `type="text"` with an appropriate [`inputMode`](https://developer.mozilla.org/docs/Web/HTML/Global_attributes/inputmode). RIFM does not support `type="number"` or `type="date"` because those controls do not expose the selection APIs needed to restore the caret.
- const rifm = useRifm({
- value,
- onChange: setValue,
- format: numberFormat
- })
+## Render-prop API
-
+If hooks are not convenient, use the `Rifm` component. It accepts the same options and passes input props to its child function.
-...
-```
+```tsx
+import { useState } from "react";
+import { Rifm } from "rifm";
-## Install
+export function PriceInput() {
+ const [value, setValue] = useState("");
-```sh
-pnpm add rifm
+ return (
+
+ {({ value, onChange }) => (
+
+ )}
+
+ );
+}
```
+Because RIFM only supplies `value` and `onChange`, the input can be a native element or any component that accepts compatible props.
+
## API
-### Terminology
+Both `useRifm(options)` and `` accept the following options.
+
+### Required options
+
+| Option | Type | Description |
+| ---------- | --------------------------- | ------------------------------------- |
+| `value` | `string` | The controlled input value. |
+| `onChange` | `(value: string) => void` | Called with the next formatted value. |
+| `format` | `(value: string) => string` | Formats the value after every edit. |
+
+### Optional options
+
+| Option | Type | Default | Description |
+| ---------- | --------------------------- | ------- | ---------------------------------------------------------------------------------------------------------------- |
+| `accept` | `RegExp` | `/\d/g` | Matches characters whose order RIFM tracks when restoring the caret. Use a global regular expression. |
+| `replace` | `(value: string) => string` | — | Post-processes the formatted value while preserving the caret, for example to enforce uppercase or replace text. |
+| `append` | `(value: string) => string` | — | Post-processes insertions made at the end; useful when a formatter needs to append a separator. |
+| `mask` | `boolean` | — | Enables replacement-style mask editing and caret movement across mask separators. |
+| `children` | `(props) => ReactNode` | — | Required by `` only. Receives `{ value, onChange }`. |
+
+### Return value
+
+`useRifm` returns the same object passed to the `Rifm` child function:
+
+```ts
+{
+ value: string;
+ onChange: React.ChangeEventHandler;
+}
+```
-Rifm is based on simple idea (**\***):
+Pass both properties to the underlying input.
-- format operation applied to input value after edit doesn't change the order of some symbols before cursor
+## Accepted characters and caret behavior
-**\*** _This is not always true, but we solve some edge cases where it's not._
+RIFM restores the caret by tracking the characters matched by `accept`. The formatter may insert, remove, or move separators, but it should preserve the order of those accepted characters.
-> Imagine you have simple integer number formatter with **\`** as thousands separator
-> and current input state is _123\`4_**|**_67_ _("|" shows current cursor position)_.
->
-> User press _5_ then formatted input must be equal to _1\`234\`5_**|**_67_.
->
-> The overall order of elements has changed (was _1->2->3->\`->4->..._ became _1->\`->2->3->4..._)
-> but the order of digits before cursor hasn't changed (was _1->2->3->4_ and hasn't changed).
+For example, with digits as accepted characters:
+
+```text
+Before typing: 1,234|67
+Type "5": 1,2345|67
+Formatted: 1,234,5|67
+```
-The same is true for float numbers formatting, dates and more.
-Symbols with preserved order are different and depends on format.
-We call this kind of symbols - **"accepted"** symbols.
+The commas move, but the digits before the caret remain in the same order, so RIFM can find the correct position. Set `accept` when formatting something other than digits:
-Rifm solves only one task -
-find the right place for cursor after formatting.
+```tsx
+const rifm = useRifm({
+ value,
+ onChange: setValue,
+ format: (value) => value,
+ replace: (value) => value.replace(/[^a-z ]/gi, "").toUpperCase(),
+ accept: /[a-z ]/gi,
+});
+```
-Knowledge about what symbols are **"accepted"** and cursor position after any user action
-is enough to find the final cursor position.
+Use `replace` for transformations such as case enforcement that change accepted characters themselves.
-Most operations which are not covered with above idea like
-case enforcements, masks guides, floating point _","=>"."_ replacement
-can be done using simple postprocessing step - replace.
-This operation works well if you need to change input value without loosing cursor position.
+## TypeScript
-And finaly masks - masks are usually is format with replace editing mode + some small cursor visual hacks.
+The hook and component can infer common input types. For an explicit custom element or component event type, provide the generic parameter:
-### Input Props
+```tsx
+const inputProps = useRifm({
+ value,
+ onChange: setValue,
+ format: formatInteger,
+});
+```
-These are accepted by the Rifm component as props and the useRifm hook as named arguments.
+## Development
-| Prop | type | default | Description |
-| ------------ | :---------------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| **accept** | RegExp (optional) | /\d/g | Regular expression to detect **"accepted"** symbols |
-| **format** | string => string | | format function |
-| **value** | string | | input value |
-| **onChange** | string => void | | event fired on input change |
-| **children** | ({ value, onChange }) => Node | | value and onChange handler you need to pass to underlying input element |
-| **mask** | boolean (optional) | | use replace input mode if true, use cursor visual hacks if prop provided |
-| **replace** | string => string (optional) | | format postprocessor allows you to fully replace any/all symbol/s preserving cursor |
-| **append** | string => string (optional) | | format postprocessor called only if cursor is in the last position and new symbols added, used for specific use-case to add non accepted symbol when you type |
+```sh
+pnpm install
+pnpm test
+pnpm run build
+```
-### Output Props
+The test suite includes TypeScript, unit, and browser tests.
-These will be passed into the `children` render prop for the Rifm component as named arguments, and returned from the useRifm hook as an object.
+## License
-| Prop | type | default | Description |
-| ------------ | :--------------------- | :------ | :--------------------------------------------------------------- |
-| **value** | string | | A formatted string value to pass as a prop to your input element |
-| **onChange** | SyntheticEvent => void | | The change handler to pass as a prop to your input element |
+[MIT](LICENSE)