Skip to content

Export renderer#4

Closed
kaznaan wants to merge 1 commit intokaz/cull-viewportfrom
kaz/export-renderer
Closed

Export renderer#4
kaznaan wants to merge 1 commit intokaz/cull-viewportfrom
kaz/export-renderer

Conversation

@kaznaan
Copy link
Copy Markdown
Collaborator

@kaznaan kaznaan commented Feb 18, 2026

Export ShapeRenderer as a customizable component.

@cursor
Copy link
Copy Markdown

cursor Bot commented Feb 18, 2026

PR Summary

Medium Risk
Touches core canvas shape rendering; behavior should be equivalent by default, but custom renderers or subtle ordering/keying differences could impact rendering/performance.

Overview
Adds a new customizable ShapeRenderer component to the editor’s component override system, letting consumers control how visible shapes are iterated/rendered.

DefaultCanvas no longer maps editor.getVisibleRenderingShapes() directly; it delegates to the injected ShapeRenderer (defaulting to new DefaultShapeRenderer) via a renderShape callback. This new API surface is exported from @tldraw/editor (including TLShapeRendererProps) and reflected in the API report.

Written by Cursor Bugbot for commit b7ccf05. This will update automatically on new commits. Configure here.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON. A Cloud Agent has been kicked off to fix the reported issue.

editor,
])
const { ShapeRenderer } = useEditorComponents()
const Renderer = ShapeRenderer ?? DefaultShapeRenderer
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Null ShapeRenderer silently falls back instead of disabling

Medium Severity

The ShapeRenderer type includes | null, following the convention of other editor components where null means "disable this component." However, the consumption site uses ShapeRenderer ?? DefaultShapeRenderer, which silently falls back to the default when null is provided. Every other nullable component in the codebase uses if (!Component) return null or {Component && ...} to respect the null-means-disabled contract. This breaks the established API contract — a user passing ShapeRenderer: null would expect shape rendering to be disabled but would instead get the default renderer.

Additional Locations (1)

Fix in Cursor Fix in Web

@cursor
Copy link
Copy Markdown

cursor Bot commented Feb 18, 2026

Bugbot Autofix prepared fixes for 1 of the 1 bugs found in the latest run.

  • ✅ Fixed: Null ShapeRenderer silently falls back instead of disabling
    • Changed the ShapesToDisplay function to check for null and return null (respecting the disabled contract) instead of using nullish coalescing which fell back to DefaultShapeRenderer.

Create PR

Or push these changes by commenting:

@cursor push 18ea1d25c4
Preview (18ea1d25c4)
diff --git a/packages/editor/src/lib/components/default-components/DefaultCanvas.tsx b/packages/editor/src/lib/components/default-components/DefaultCanvas.tsx
--- a/packages/editor/src/lib/components/default-components/DefaultCanvas.tsx
+++ b/packages/editor/src/lib/components/default-components/DefaultCanvas.tsx
@@ -4,6 +4,7 @@
 import { dedupe, modulate, objectMapValues } from '@tldraw/utils'
 import classNames from 'classnames'
 import { Fragment, JSX, ReactElement, useEffect, useRef, useState } from 'react'
+import type { TLRenderingShape } from '../../editor/Editor'
 import { tlenv } from '../../globals/environment'
 import { useCanvasEvents } from '../../hooks/useCanvasEvents'
 import { useCoarsePointer } from '../../hooks/useCoarsePointer'
@@ -26,7 +27,6 @@
 import { LiveCollaborators } from '../LiveCollaborators'
 import { MenuClickCapture } from '../MenuClickCapture'
 import { Shape } from '../Shape'
-import type { TLRenderingShape } from '../../editor/Editor'
 
 /** @public */
 export interface TLCanvasComponentProps {
@@ -435,11 +435,12 @@
 
 function ShapesToDisplay() {
 	const { ShapeRenderer } = useEditorComponents()
-	const Renderer = ShapeRenderer ?? DefaultShapeRenderer
 
+	if (!ShapeRenderer) return tlenv.isSafari ? <ReflowIfNeeded /> : null
+
 	return (
 		<>
-			<Renderer renderShape={(shape) => <Shape key={shape.id + '_shape'} {...shape} />} />
+			<ShapeRenderer renderShape={(shape) => <Shape key={shape.id + '_shape'} {...shape} />} />
 			{tlenv.isSafari && <ReflowIfNeeded />}
 		</>
 	)

@kaznaan kaznaan closed this Feb 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant