React Data GridStar Rating Cell Type - Step-by-Step Guide (React)
- Overview
- What You'll Build
- Complete Example
- Prerequisites
- Step 1: Import Dependencies
- Step 2: Create the Editor Component
- Step 3: Add a Custom Renderer for View Mode
- Step 4: Add a Validator (Optional)
- Step 5: Add Styling
- Step 6: Prepare Sample Data
- Step 7: Use in Handsontable
- How It Works - Complete Flow
- Enhancements
- Accessibility
- Performance Considerations
- TypeScript Support
- Best Practices
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-wrapperpackagereact-star-rating-componentpackage 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 editorsHotTableandHotColumn- React wrapper componentsStarRatingComponent- Star rating UI fromreact-star-rating-componentregisterAllModules()- 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:
EditorComponentwraps your editor UI; thechildrenprop is a function that receives editor state.value- Current cell value (numeric rating)setValue- Function to update the valuefinishEditing- Function to save and close the editoronStarHover- Updates preview as user hovers over starsonStarClick- Saves the selected rating and closes the editor- The
rating-editordiv is inside the render prop so styling applies to the visible editor area.
Key concepts:
- Render prop pattern:
EditorComponentuses a function as children - Hover preview:
onStarHoverlets users preview before committing - Click to confirm:
onStarClicksaves 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
valueand displays it withStarRatingComponent 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-cellaligns the stars in the cell when not editing.rating-editoruses 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, andprice - The
ratingcolumn 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 activerenderer={RatingCellRenderer}- Shows stars in view modevalidator={ratingValidator}- Ensures rating is within the allowed range (e.g., 0–100)data="rating"- Binds to theratingproperty 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
- Initial Render:
RatingCellRendererdisplays stars for the current rating (e.g., 3 filled stars). - User Double-Clicks or Enter: Editor opens.
- Editor Opens:
EditorComponentshows the star picker in the cell. - Star Rating Display: Stars show current value; empty stars show remaining.
- User Interaction:
- Hover over stars →
onStarHoverupdates preview viasetValue - Click a star →
onStarClicksaves value and callsfinishEditing()
- Hover over stars →
- Validation:
ratingValidatorruns (e.g., value must be 0–100). - Save: Numeric value is saved to the cell.
- Editor Closes:
RatingCellRenderershows 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
- Lightweight library: react-star-rating-component is small and focused
- React Virtual DOM: Efficient updates only when value changes
- Simple callbacks:
onStarHoverandonStarClickare straightforward - 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
- Coerce value to number - Use
Number(value) || 0since cell values may be strings. - Provide
nameprop - Required byreact-star-rating-componentfor radio inputs; use different names for editor and renderer (e.g."rating"and"rating-cell") to avoid conflicts. - Call
finishEditing()on click - Star click confirms the selection and closes the editor. - Use
onStarHoverfor preview - Improves UX by showing the selection before commit. - Use a custom renderer -
RatingCellRendererwithediting={false}shows stars in view mode and keeps the UI consistent. - Add a validator - Use
ratingValidatorto 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!