npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

emin-lexical-editor-nohut

v1.0.32

Published

Lexical Editor with Equation Editor

Readme

Emin Lexical Editor

A modern, feature-rich, and easy-to-use text editor for React, built on top of Lexical. It supports controlled content management, light/dark themes, and file uploads.

npm install emin-lexical-editor-nohut

or

yarn add emin-lexical-editor-nohut

Usage

The EminEditor is a controlled component. Its content is managed by the editorState prop, which must be a serialized Lexical JSON string. This approach ensures reliable and predictable state management.

Here is a complete usage example with React's useState hook:

import React, { useState } from 'react';
import { EminEditor } from 'emin-lexical-editor-nohut';

// A sample serialized editor state. You can get this from the onChange event or your database.
// For a new, empty editor, you can pass an empty string "".
const SAMPLE_EDITOR_STATE = '{"root":{"children":[{"children":[{"detail":0,"format":0,"mode":"normal","style":"","text":"This is a sample content loaded externally!","type":"text","version":1}],"direction":"ltr","format":"","indent":0,"type":"paragraph","version":1}],"direction":"ltr","format":"","indent":0,"type":"root","version":1}}';

function MyEditorComponent() {
  // Manage the editor's state using React's useState hook.
  const [editorState, setEditorState] = useState<string>('');

  // The onChange handler provides the latest content in HTML and serialized JSON format.
  // It's crucial to update your state here to keep the component controlled.
  const handleChange = (data: { html: string; serialized: SerializedEditorState; charCount: number }) => {
    // It's crucial to update your state with the serialized JSON to keep the component controlled.
    setEditorState(JSON.stringify(data.serialized));
    
    // You also get the clean HTML output.
    console.log('HTML Output:', data.html);
    console.log('Character Count:', data.charCount);
  };

  const loadSampleContent = () => {
    setEditorState(SAMPLE_EDITOR_STATE);
  };

  return (
    <div>
      <button onClick={loadSampleContent}>Load Sample Content</button>
      
      <EminEditor 
        editorState={editorState}
        onChange={handleChange}
        placeholder="Start typing..."
        height="500px"
        theme="light" // or "dark"
      />
    </div>
  );
}

export default MyEditorComponent;

HTML Serialization

The editor ensures that custom elements are correctly serialized into clean, portable HTML. This is crucial for storing and rendering content outside the editor.

  • LaTeX Equations: Rendered as <span> tags with a data-latex attribute, preserving the original LaTeX code.
    <span data-latex="\sum_{i=1}^{n} x_i">...</span>
  • Media Elements: Images, videos, and audio are serialized to their standard HTML tags (<img>, <video>, <audio>) with src, alt, title, and other attributes intact.
    <img src="/path/to/image.png" alt="Description">
    <video src="/path/to/video.mp4" controls></video>

Features

  • Controlled Component: Reliably manage content with the editorState prop.
  • Rich Text Editing: Bold, Italic, Underline, Strikethrough, etc.
  • Headings & Lists: Structure your content with H1, H2, H3, and ordered/unordered lists.
  • Link Insertion: Easily add and edit hyperlinks.
  • File Uploads: Integrated toolbar button for uploading images, videos, and audio files (requires fileUploadConfig).
  • LaTeX Equations: Inline and block equation support.
  • Customizable: Change theme (light/dark) and height.
  • Callbacks: onChange provides HTML, serialized state, and character count.

Props

| Prop | Type | Default | Description | | ---------------- | ----------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------ | | editorState | string | '' | (Required) A serialized Lexical JSON string to control the editor's content. | | onChange | (data: { html: string; serialized: SerializedEditorState; charCount: number; }) => void | () => {} | Callback that fires on change, providing HTML, serialized state, and character count. | | placeholder | string | '' | The placeholder text to display when the editor is empty. | | height | string | '400px' | The height of the editor container (e.g., '500px'). | | theme | 'light' \| 'dark' | 'light' | The visual theme of the editor. | | fileUploadConfig | object | undefined | File upload configuration. See details below. | | toolbarToggle | boolean | false | If true, the toolbar is hidden by default and appears on hover over the editor area. If false, it's always visible. |

interface FileUploadConfig {
  // --- Core Fields ---
  uploadUrl: string; // Your backend endpoint.
  token?: string; // Optional auth token.

  // --- API & Upload Strategy ---
  apiType?: 'rest' | 'graphql'; // default: 'rest'
  uploadMethod?: 'file' | 'base64'; // default: 'file'

  // --- GraphQL Specific ---
  graphqlMutation?: string; // The GraphQL mutation query.
  graphqlVariableName?: string; // The variable name for the file in the mutation. default: 'file'

  // --- Callbacks & Response Handling ---
  onUploadStart?: (file: File) => void;
  onUploadSuccess?: (response: any, file: File) => void;
  onUploadError?: (error: string, file: File) => void;
  responseUrlExtractor?: (response: any) => string; // Extracts the URL from the server response.
}

Usage Examples

1. Simple REST Upload (Default)

This is the simplest configuration. The editor sends the file as multipart/form-data to a REST endpoint.

const restConfig = {
  uploadUrl: 'http://your-backend.com/upload',
  token: 'your-auth-token',
  responseUrlExtractor: (res) => res.fileUrl // Extracts URL from { fileUrl: '...' }
};

<EminEditor fileUploadConfig={restConfig} />

Your backend should expect a POST request with a file field in the FormData.

2. REST Upload with Base64

To send the file as a Base64 string within a JSON payload:

const base64RestConfig = {
  uploadUrl: 'http://your-backend.com/upload-base64',
  token: 'your-auth-token',
  apiType: 'rest',
  uploadMethod: 'base64',
  responseUrlExtractor: (res) => res.url
};

<EminEditor fileUploadConfig={base64RestConfig} />

Your backend will receive a JSON body like this: { "file": "data:image/png;base64,...", "fileName": "...", "fileType": "..." }.

3. GraphQL Upload (File)

The editor supports the GraphQL multipart request specification for native file uploads.

const gqlConfig = {
  uploadUrl: 'http://your-api.com/graphql',
  apiType: 'graphql',
  uploadMethod: 'file', // This is the default
  graphqlMutation: `
    mutation($file: Upload!) {
      uploadFile(file: $file) {
        url
        filename
      }
    }
  `,
  responseUrlExtractor: (res) => res.data.uploadFile.url
};

<EminEditor fileUploadConfig={gqlConfig} />

4. GraphQL Upload (Base64)

You can also send the file as a Base64 string to a GraphQL endpoint.

const gqlBase64Config = {
  uploadUrl: 'http://your-api.com/graphql',
  apiType: 'graphql',
  uploadMethod: 'base64',
  graphqlMutation: `
    mutation($file: String!) {
      uploadBase64(base64: $file) {
        url
      }
    }
  `,
  graphqlVariableName: 'file', // Optional, defaults to 'file'
  responseUrlExtractor: (res) => res.data.uploadBase64.url
};

<EminEditor fileUploadConfig={gqlBase64Config} />

TypeScript Support

The package includes full TypeScript definitions:

import { EminEditor, EminEditorProps } from 'emin-lexical-editor-nohut';

const MyEditor: React.FC<EminEditorProps> = (props) => {
  return <EminEditor {...props} />;
};


## License

MIT