JavaScript Data GridStar Rating Cell Type - Step-by-Step Guide (React)

Overview

This guide shows how to create a star rating editor cell using react-star-rating-component with React's EditorComponent. Perfect for product reviews, feedback forms, or any scenario where users need to select a numeric rating (e.g., 1–5 stars).

Difficulty: Beginner Time: ~15 minutes Libraries: react-star-rating-component

What You'll Build

A cell that:

  • Displays interactive star rating when editing
  • Shows stars in view mode via a custom React renderer
  • Supports hover preview before selection
  • Stores values as numbers (1–5)
  • Validates rating range (e.g., 0–100)
  • Provides click-to-select functionality
  • Works with React's component-based architecture

Complete Example

Prerequisites

npm install @handsontable/react-wrapper react-star-rating-component

What you need:

  • React 16.8+ (hooks support)
  • @handsontable/react-wrapper package
  • react-star-rating-component package for the star rating UI
  • Basic React knowledge (hooks, JSX)

Step 1: Import Dependencies

import { HotTable, HotColumn, EditorComponent } from '@handsontable/react-wrapper';
import { registerAllModules } from 'handsontable/registry';
import StarRatingComponent from 'react-star-rating-component';

registerAllModules();

What we're importing:

  • EditorComponent - React component for creating custom editors
  • HotTable and HotColumn - React wrapper components
  • StarRatingComponent - Star rating UI from react-star-rating-component
  • registerAllModules() - Registers Handsontable modules (required when using the wrapper)

Step 2: Create the Editor Component

Create a React component that uses EditorComponent with the render prop pattern.

export const RatingEditor = () => {
  return (
    <EditorComponent<number>>
      {({ value, setValue, finishEditing }) => (
        <div className="rating-editor">
          <StarRatingComponent
            name="rating"
            value={Number(value) || 0}
            onStarHover={(nextValue: number) => setValue(nextValue)}
            onStarClick={(nextValue: number) => {
              setValue(nextValue);
              finishEditing();
            }}
          />
        </div>
      )}
    </EditorComponent>
  );
};

What's happening:

  1. EditorComponent wraps your editor UI; the children prop is a function that receives editor state.
  2. value - Current cell value (numeric rating)
  3. setValue - Function to update the value
  4. finishEditing - Function to save and close the editor
  5. onStarHover - Updates preview as user hovers over stars
  6. onStarClick - Saves the selected rating and closes the editor
  7. The rating-editor div is inside the render prop so styling applies to the visible editor area.

Key concepts:

  • Render prop pattern: EditorComponent uses a function as children
  • Hover preview: onStarHover lets users preview before committing
  • Click to confirm: onStarClick saves and closes the editor

Step 3: Add a Custom Renderer for View Mode

Use a React component as the cell renderer so stars are shown when not editing.

const RatingCellRenderer = ({ value }: { value: unknown }) => (
  <div className="rating-cell">
    <StarRatingComponent
      name="rating-cell"
      value={Number(value) || 0}
      editing={false}
    />
  </div>
);

What's happening:

  • The renderer receives value and displays it with StarRatingComponent
  • editing={false} keeps the stars non-interactive in view mode
  • Use a unique name (e.g. "rating-cell") to avoid conflicts with the editor instance

Step 4: Add a Validator (Optional)

Validate that the rating is within an allowed range (e.g., 0–100):

const ratingValidator = (value: string | number, callback: (valid: boolean) => void) => {
  const parsed = parseInt(String(value));
  callback(parsed >= 0 && parsed <= 100);
};

For a strict 1–5 star scale, use parsed >= 1 && parsed <= 5 instead.

Step 5: Add Styling

Style the cell and editor so the star rating fits and matches the grid.

.rating-cell {
  display: flex;
  align-items: center;
  margin: 3px 0 0 -1px;
}

.rating-editor {
  display: flex;
  align-items: center;
  height: 100%;
  box-sizing: border-box !important;
  border: none;
  border-radius: 0;
  box-shadow: inset 0 0 0 var(--ht-cell-editor-border-width, 2px)
    var(--ht-cell-editor-border-color, #1a42e8),
    0 0 var(--ht-cell-editor-shadow-blur-radius, 0) 0
    var(--ht-cell-editor-shadow-color, transparent);
  background-color: var(--ht-cell-editor-background-color, #ffffff);
  padding: var(--ht-cell-vertical-padding, 4px)
    var(--ht-cell-horizontal-padding, 8px);
  font-family: var(--ht-font-family, inherit);
  font-size: var(--ht-font-size, 14px);
  line-height: var(--ht-line-height, 1.5);
}

What's happening:

  • .rating-cell aligns the stars in the cell when not editing
  • .rating-editor uses Handsontable CSS variables for focus border, background, and padding so the editor matches the grid theme

Step 6: Prepare Sample Data

Use data with a rating property (and any other columns you need). Example for a product table:

export const data = [
  { product: "Dashboard Pro", category: "Analytics", rating: 5, reviews: 342, price: 49 },
  { product: "Form Builder", category: "Tools", rating: 4, reviews: 218, price: 29 },
  { product: "Chart Engine", category: "Analytics", rating: 3, reviews: 156, price: 39 },
  { product: "Auth Module", category: "Security", rating: 5, reviews: 89, price: 19 },
  { product: "File Manager", category: "Storage", rating: 2, reviews: 64, price: 15 },
  { product: "Email Service", category: "Communication", rating: 4, reviews: 275, price: 25 },
  { product: "Search Index", category: "Tools", rating: 1, reviews: 31, price: 35 },
  { product: "Cache Layer", category: "Infra", rating: 4, reviews: 112, price: 20 },
];

What's happening:

  • Each row has product, category, rating, reviews, and price
  • The rating column uses the star editor and renderer; other columns can be text or numeric

Step 7: Use in Handsontable

Wire the editor, renderer, and validator to the rating column:

const ExampleComponent = () => {
  return (
    <HotTable
      data={data}
      colHeaders={["Product", "Category", "Rating", "Reviews", "Price"]}
      autoRowSize={true}
      rowHeaders={true}
      height="auto"
      width="100%"
      autoWrapRow={true}
      headerClassName="htLeft"
      licenseKey="non-commercial-and-evaluation"
    >
      <HotColumn data="product" type="text" width={240} />
      <HotColumn data="category" type="text" width={120} />
      <HotColumn
        data="rating"
        width={150}
        editor={RatingEditor}
        renderer={RatingCellRenderer}
        validator={ratingValidator}
      />
      <HotColumn data="reviews" type="numeric" width={80} />
      <HotColumn data="price" type="numeric" width={80} />
    </HotTable>
  );
};

What's happening:

  • editor={RatingEditor} - Star rating editor when the cell is active
  • renderer={RatingCellRenderer} - Shows stars in view mode
  • validator={ratingValidator} - Ensures rating is within the allowed range (e.g., 0–100)
  • data="rating" - Binds to the rating property in each row

Key features:

  • Stars in both view and edit mode
  • Values stored as numbers (1–5)
  • Validation and type-safe setup with TypeScript

How It Works - Complete Flow

  1. Initial Render: RatingCellRenderer displays stars for the current rating (e.g., 3 filled stars).
  2. User Double-Clicks or Enter: Editor opens.
  3. Editor Opens: EditorComponent shows the star picker in the cell.
  4. Star Rating Display: Stars show current value; empty stars show remaining.
  5. User Interaction:
    • Hover over stars → onStarHover updates preview via setValue
    • Click a star → onStarClick saves value and calls finishEditing()
  6. Validation: ratingValidator runs (e.g., value must be 0–100).
  7. Save: Numeric value is saved to the cell.
  8. Editor Closes: RatingCellRenderer shows the updated stars in view mode.

Enhancements

1. Custom Star Count

Change the number of stars (e.g., 10-point scale):

<StarRatingComponent
  name="rating"
  starCount={10}
  value={Number(value) || 0}
  onStarHover={(nextValue) => setValue(nextValue)}
  onStarClick={(nextValue) => {
    setValue(nextValue);
    finishEditing();
  }}
/>

2. Custom Star Colors

Customize the appearance:

<StarRatingComponent
  name="rating"
  value={Number(value) || 0}
  starColor="#ffd700"
  emptyStarColor="#e0e0e0"
  onStarHover={(nextValue) => setValue(nextValue)}
  onStarClick={(nextValue) => {
    setValue(nextValue);
    finishEditing();
  }}
/>

3. Alternative: HTML-Based Renderer

The main example uses a React component (RatingCellRenderer) for view mode. If you prefer a non-React renderer, you can use rendererFactory:

import { rendererFactory } from 'handsontable/renderers';

const starRenderer = rendererFactory(({ td, value }) => {
  const rating = Number(value) || 0;
  const stars = '★'.repeat(rating) + '☆'.repeat(5 - rating);
  td.innerHTML = `
    <div style="
      font-size: 1.2em;
      color: #ffb400;
      letter-spacing: 2px;
    ">${stars}</div>
  `;
});

// Use in HotColumn
<HotColumn
  data="rating"
  width={150}
  editor={RatingEditor}
  renderer={starRenderer}
/>

4. Read Config from Cell Properties (Advanced)

Use onPrepare for per-column configuration (e.g., star count):

const RatingEditor = () => {
  const [starCount, setStarCount] = useState(5);

  const onPrepare = (_row, _column, _prop, _TD, _originalValue, cellProperties) => {
    if (cellProperties.starCount != null) {
      setStarCount(cellProperties.starCount);
    }
  };

  return (
    <div className="rating-editor">
      <EditorComponent<number> onPrepare={onPrepare}>
        {({ value, setValue, finishEditing }) => (
          <StarRatingComponent
            name="rating"
            starCount={starCount}
            value={Number(value) || 0}
            onStarHover={(nextValue) => setValue(nextValue)}
            onStarClick={(nextValue) => {
              setValue(nextValue);
              finishEditing();
            }}
          />
        )}
      </EditorComponent>
    </div>
  );
};

// Use with different star counts per column
<HotColumn editor={RatingEditor} starCount={5} data="rating" title="Rating (1-5)" />
<HotColumn editor={RatingEditor} starCount={10} data="score" title="Score (1-10)" />

5. Handle Empty Values

Ensure the component handles undefined or null:

value={Number(value) || 0}

This displays empty stars when the cell has no value.

Accessibility

The StarRatingComponent uses radio inputs. Enhance with ARIA:

<StarRatingComponent
  name="rating"
  value={Number(value) || 0}
  onStarHover={(nextValue) => setValue(nextValue)}
  onStarClick={(nextValue) => {
    setValue(nextValue);
    finishEditing();
  }}
  aria-label="Select rating"
/>

Keyboard navigation:

  • Tab: Navigate to editor
  • Arrow keys: Navigate between stars (if supported by library)
  • Enter/Space: Select star
  • Escape: Cancel editing

Performance Considerations

Why This Is Fast

  1. Lightweight library: react-star-rating-component is small and focused
  2. React Virtual DOM: Efficient updates only when value changes
  3. Simple callbacks: onStarHover and onStarClick are straightforward
  4. No unnecessary re-renders: Editor unmounts when closed

TypeScript Support

EditorComponent is fully typed. Specify the value type for numeric ratings:

<EditorComponent<number>>
  {({ value, setValue, finishEditing }) => {
    // TypeScript knows value is number | undefined
    // TypeScript knows setValue accepts number
    return (
      <StarRatingComponent
        name="rating"
        value={Number(value) || 0}
        onStarHover={(nextValue: number) => setValue(nextValue)}
        onStarClick={(nextValue: number) => {
          setValue(nextValue);
          finishEditing();
        }}
      />
    );
  }}
</EditorComponent>

Best Practices

  1. Coerce value to number - Use Number(value) || 0 since cell values may be strings.
  2. Provide name prop - Required by react-star-rating-component for radio inputs; use different names for editor and renderer (e.g. "rating" and "rating-cell") to avoid conflicts.
  3. Call finishEditing() on click - Star click confirms the selection and closes the editor.
  4. Use onStarHover for preview - Improves UX by showing the selection before commit.
  5. Use a custom renderer - RatingCellRenderer with editing={false} shows stars in view mode and keeps the UI consistent.
  6. Add a validator - Use ratingValidator to restrict values (e.g., 0–100 or 1–5) and give immediate feedback.

Congratulations! You've created a star rating editor using React's EditorComponent and react-star-rating-component, perfect for rating selection in your data grid!