Home

Awesome

rich-textarea

npm npm bundle size npm check demo

A small customizable textarea for React to colorize, highlight, decorate texts, offer autocomplete and much more.

<img src="./images/sample-mention.gif" width="350px" /><img src="./images/sample-diff.gif" width="350px" />

<img src="./images/sample-tagging.gif" width="350px" /><img src="./images/sample-url.gif" width="350px" />

<img src="./images/sample-prism.gif" width="350px" /><img src="./images/sample-kuromoji.gif" width="350px" />

Demo

https://inokawa.github.io/rich-textarea/

Features

Motivation

Sometimes we need customized text editor in web. However creating it with raw contenteditable is so hard to do properly and editor frameworks are usually too heavy... Maybe you really need is just a textarea with highlighting and some hovered menus, but native textarea and many of textarea libraries are far from it because of the limited customizability. This library is aiming to solve the problem.

Install

npm install rich-textarea

Requirements

If you use ESM and webpack 5, use react >= 18 to avoid Can't resolve react/jsx-runtime error.

Usage

Controlled

import { useState } from "react";
import { RichTextarea } from "rich-textarea";

export const App = () => {
  const [text, setText] = useState("Lorem ipsum");

  return (
    <RichTextarea
      value={text}
      style={{ width: "600px", height: "400px" }}
      onChange={(e) => setText(e.target.value)}
    >
      {(v) => {
        return v.split("").map((t, i) => (
          <span key={i} style={{ color: i % 2 === 0 ? "red" : undefined }}>
            {t}
          </span>
        ));
      }}
    </RichTextarea>
  );
};

Uncontrolled

import { RichTextarea } from "rich-textarea";

export const App = () => {
  return (
    <RichTextarea
      defaultValue="Lorem ipsum"
      style={{ width: "600px", height: "400px" }}
    >
      {(v) => {
        return v.split("").map((t, i) => (
          <span key={i} style={{ color: i % 2 === 0 ? "red" : undefined }}>
            {t}
          </span>
        ));
      }}
    </RichTextarea>
  );
};

Regex helper

You can use helper for regex if you don't want to create your own render function.

import { useState } from "react";
import { RichTextarea, createRegexRenderer } from "rich-textarea";

const renderer = createRegexRenderer([
  [/[A-Z][a-z]+/g, { borderRadius: "3px", backgroundColor: "#d0bfff" }],
]);

export const App = () => {
  const [text, setText] = useState("Lorem ipsum");

  return (
    <RichTextarea
      value={text}
      style={{ width: "600px", height: "400px" }}
      onChange={(e) => setText(e.target.value)}
    >
      {renderer}
    </RichTextarea>
  );
};

Next.js Server Actions

// _components/Textarea.tsx
"use client";
import { RichTextarea, RichTextareaProps } from "rich-textarea";

export const Textarea = (
  props: Omit<RichTextareaProps, "children" | "ref">
) => {
  return (
    <RichTextarea {...props}>
      {(v) => {
        return v.split("").map((t, i) => (
          <span key={i} style={{ color: i % 2 === 0 ? "red" : undefined }}>
            {t}
          </span>
        ));
      }}
    </RichTextarea>
  );
};

// page.tsx in App Router of Next.js
import { Textarea } from "../_components/Textarea";

async function hello(formData: FormData) {
  "use server";
  console.log(formData.get("hello"));
}

export default () => {
  return (
    <div>
      <div>this is server!</div>
      <form action={hello}>
        <Textarea defaultValue="Lorem ipsum" name="hello" />
        <button type="submit">submit</button>
      </form>
    </div>
  );
};

Remix action

import type { ActionFunction } from "@remix-run/node";
import { Form } from "@remix-run/react";
import { RichTextarea, RichTextareaProps } from "rich-textarea";

const Textarea = (props: Omit<RichTextareaProps, "children" | "ref">) => {
  return (
    <RichTextarea {...props}>
      {(v) => {
        return v.split("").map((t, i) => (
          <span key={i} style={{ color: i % 2 === 0 ? "red" : undefined }}>
            {t}
          </span>
        ));
      }}
    </RichTextarea>
  );
};

export const action: ActionFunction = async ({ request }) => {
  console.log((await request.formData()).get("hello"));
};

export default () => {
  return (
    <Form method="post">
      <Textarea defaultValue="Lorem ipsum" name="hello" />
      <button type="submit">submit</button>
    </Form>
  );
};

Documentation

Contribute

All contributions are welcome. If you find a problem, feel free to create an issue or a PR.

Making a Pull Request

  1. Fork this repo.
  2. Run npm install.
  3. Commit your fix.
  4. Make a PR and confirm all the CI checks passed.