I am creating a carousel component where I want to give user option to drag drop component in carousel container from builder or also in code too. I added list of uiBlocks as input but when I drag and drop any other component page gets blank. component registration code as below:
Builder.registerComponent(Carousel, {
name: 'Carousel',
canHaveChildren: true,
inputs: [
{
name: 'useBuilderComponents',
type: 'boolean',
defaultValue: false,
friendlyName: 'Use Manual Slides Mode',
helperText: 'When enabled, add slide components manually as children. When disabled, use the slides list below.',
},
{
name: 'slides',
type: 'list',
showIf: 'options.get("useBuilderComponents") !== true',
helperText: 'Configure slides with content - each slide will be a container for components',
defaultValue: [
{
slideTitle: 'Slide 1',
slideContent: [],
},
{
slideTitle: 'Slide 2',
slideContent: [],
},
],
subFields: [
{
name: 'slideTitle',
type: 'string',
defaultValue: 'Slide Title',
helperText: 'Title for this slide (for reference)',
},
{
name: 'slideContent',
type: 'uiBlocks',
hideFromUI: false,
defaultValue: [],
helperText: 'Drop components here for this slide',
},
],
},
{
name: 'autoplay',
type: 'boolean',
defaultValue: false,
helperText: 'Enable automatic slideshow',
},
{
name: 'autoplaySpeed',
type: 'number',
defaultValue: 3000,
min: 1000,
max: 10000,
helperText: 'Autoplay interval in milliseconds',
showIf: 'options.get("autoplay") === true',
},
{
name: 'showArrows',
type: 'boolean',
defaultValue: true,
helperText: 'Show navigation arrows',
},
{
name: 'showDots',
type: 'boolean',
defaultValue: true,
helperText: 'Show dot indicators',
},
{
name: 'infinite',
type: 'boolean',
defaultValue: true,
helperText: 'Enable infinite loop',
},
{
name: 'slidesToShow',
type: 'number',
defaultValue: 1,
min: 1,
max: 4,
helperText: 'Number of slides to show at once',
},
{
name: 'slidesToScroll',
type: 'number',
defaultValue: 1,
min: 1,
max: 4,
helperText: 'Number of slides to scroll at once',
},
{
name: 'effect',
type: 'string',
enum: [
{ label: 'Slide', value: 'slide' },
{ label: 'Fade', value: 'fade' },
],
defaultValue: 'slide',
helperText: 'Transition effect between slides',
},
{
name: 'height',
type: 'string',
enum: [
{ label: 'Small (200px)', value: 'small' },
{ label: 'Medium (300px)', value: 'medium' },
{ label: 'Large (400px)', value: 'large' },
{ label: 'Extra Large (500px)', value: 'extra-large' },
{ label: 'Full Height (100vh)', value: 'full-height' },
],
defaultValue: 'medium',
helperText: 'Height of the carousel',
},
{
name: 'pauseOnHover',
type: 'boolean',
defaultValue: true,
helperText: 'Pause autoplay when hovering',
showIf: 'options.get("autoplay") === true',
},
{
name: 'swipeable',
type: 'boolean',
defaultValue: true,
helperText: 'Enable touch/swipe gestures',
},
{
name: 'speed',
type: 'number',
defaultValue: 300,
min: 100,
max: 1000,
helperText: 'Speed of transitions in milliseconds',
},
{
name: 'initialSlide',
type: 'number',
defaultValue: 0,
min: 0,
helperText: 'Starting slide index',
},
{
name: 'ariaLabel',
type: 'string',
defaultValue: 'Carousel',
helperText: 'Accessibility label for screen readers',
},
{
name: 'testId',
type: 'string',
helperText: 'Test ID for automated testing (optional)',
},
],
childRequirements: {
message: 'You can drag and drop any components here to create carousel slides',
query: {},
},
})
And Carousel Component is as below:
export const Carousel: React.FC<CarouselProps> = ({
useBuilderComponents = false,
slides = [],
builderBlock,
children,
autoplay = false,
autoplaySpeed = 3000,
showArrows = true,
showDots = true,
infinite = true,
slidesToShow = 1,
slidesToScroll = 1,
effect = 'slide',
height = 'medium',
pauseOnHover = true,
swipeable = true,
speed = 300,
initialSlide = 0,
className = '',
ariaLabel = 'Carousel',
testId,
}) => {
const [currentSlide, setCurrentSlide] = useState(initialSlide)
const [isAutoplayPaused, setIsAutoplayPaused] = useState(false)
const [isTransitioning, setIsTransitioning] = useState(false)
const [isDragging, setIsDragging] = useState(false)
const [dragStart, setDragStart] = useState(0)
const [dragOffset, setDragOffset] = useState(0)
const carouselRef = useRef<HTMLDivElement>(null)
const wrapperRef = useRef<HTMLDivElement>(null)
const autoplayRef = useRef<NodeJS.Timeout | null>(null)
const touchStartRef = useRef(0)
// Determine slide data based on mode
const slideData = useBuilderComponents ? React.Children.toArray(children) : slides || []
const totalSlides = slideData.length
const maxSlide = infinite ? totalSlides : Math.max(0, totalSlides - slidesToShow)
if (!totalSlides || totalSlides === 0) {
return (
<div className={carouselClasses} data-testid={testId}>
<div className="carousel__container">
<div className="carousel__empty">No slides to display</div>
</div>
</div>
)
}
console.log('Carousel Debug:', {
builderBlock: builderBlock?.id,
slidesCount: slides?.length,
childrenCount: React.Children.count(children),
rawSlides: slides,
mode: useBuilderComponents ? 'Manual Children' : 'Slides List',
slideData: useBuilderComponents
? React.Children.toArray(children).map((child, idx) => ({
index: idx,
type: 'Manual Child',
}))
: slides?.map((slide, idx) => ({
index: idx,
slideTitle: slide.slideTitle,
hasSlideContent: !!slide.slideContent,
slideContentCount: slide.slideContent?.length || 0,
})),
useBuilderComponents,
})
return (
<div
ref={carouselRef}
className={carouselClasses}
role="region"
aria-label={ariaLabel}
aria-live="polite"
tabIndex={0}
onKeyDown={handleKeyDown}
data-testid={testId}
>
<div className="carousel__container">
<div
ref={wrapperRef}
className={`carousel__wrapper ${isDragging ? 'carousel__wrapper--no-transition' : ''}`}
style={wrapperStyle}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
>
{slideData.map((slide, index) => {
const slideKey = useBuilderComponents ? `manual-slide-${index}` : `builder-slide-${index}`
return (
<div
key={slideKey}
className={`carousel__slide ${
effect === 'fade' && index === currentSlide ? 'carousel__slide--active' : ''
}`}
aria-hidden={effect === 'fade' ? index !== currentSlide : false}
>
{useBuilderComponents ? (
// Render manual React children when useBuilderComponents = true
React.isValidElement(slide) ? (
React.cloneElement(slide as React.ReactElement<any>, {
'data-slide-index': index,
key: slideKey,
})
) : (
<div data-slide-index={index}>{slide as React.ReactNode}</div>
)
) : (
// Render Builder.io UI blocks from slides list when useBuilderComponents = false
<div className="carousel__slide-content" key={`slide-content-${index}`}>
{(slide as any)?.slideContent && (slide as any).slideContent.length > 0 ? (
<BuilderBlocks
parentElementId={builderBlock.id}
dataPath={`slides.${index}.slideContent`}
blocks={(slide as any).slideContent}
/>
) : (
<div className="carousel__slide-placeholder">
<p>{(slide as any)?.slideTitle || `Slide ${index + 1}`}</p>
<small>Drop components here</small>
</div>
)}
</div>
)}
</div>
)
})}
</div>
{/* Navigation Arrows */}
{showArrows && totalSlides > 1 && (
<>
<button
className={`carousel__nav carousel__nav--prev ${!canGoPrev ? 'carousel__nav--disabled' : ''}`}
onClick={prevSlide}
disabled={!canGoPrev}
aria-label="Previous slide"
type="button"
>
<svg className="carousel__nav-icon" viewBox="0 0 24 24">
<path d="M15 18l-6-6 6-6v12z" />
</svg>
</button>
<button
className={`carousel__nav carousel__nav--next ${!canGoNext ? 'carousel__nav--disabled' : ''}`}
onClick={nextSlide}
disabled={!canGoNext}
aria-label="Next slide"
type="button"
>
<svg className="carousel__nav-icon" viewBox="0 0 24 24">
<path d="M9 6l6 6-6 6V6z" />
</svg>
</button>
</>
)}
{/* Dot Indicators */}
{showDots && totalSlides > 1 && (
<div className="carousel__dots" role="tablist">
{Array.from({ length: Math.ceil(totalSlides / slidesToScroll) }).map((_, index) => (
<button
key={index}
className={`carousel__dot ${
Math.floor(currentSlide / slidesToScroll) === index ? 'carousel__dot--active' : ''
}`}
onClick={() => goToSlide(index * slidesToScroll)}
aria-label={`Go to slide ${index + 1}`}
role="tab"
aria-selected={Math.floor(currentSlide / slidesToScroll) === index}
type="button"
/>
))}
</div>
)}
</div>
</div>
)
}
export default Carousel
What I am missing due to which it breaking the page. Please provide some solution for the same.