Common issues and solutions for BYO custom component development.
Component Not Rendering
Component Missing from the Module Builder
Symptoms:
- Custom Component displays an empty drop-down.
- The component is not listed in the available components.
Solution:
1. Bundle Not Uploaded
# Check if .tar.gz was uploaded
# Go to Admin → Custom Assets
# Verify your component displays in the list
2. Build Failed
# Rebuild
npm run build
# Check for errors in console
# Fix any TypeScript or build errors
3. Wrong Runtime Version
Check package.json byo.runtimeVersion matches your environment:
{
"byo": {
"runtimeVersion": "1.0.0"
}
}
Component Renders as Empty
Symptoms:
- A component space displays on the canvas.
- No content visible.
- No errors in the console.
Solution:
1. Missing Render Logic
// ❌ Bad - no render implementation
export class MyComponent extends HTMLElement {
connectedCallback() {
// Nothing here
}
}
// ✅ Good - implements render
export class MyComponent extends HTMLElement {
connectedCallback() {
this.render()
}
render() {
this.innerHTML = `<div>Hello</div>`
}
}
2. Shadow DOM Without Content
// ❌ Bad - shadow DOM but no content
connectedCallback() {
this.attachShadow({ mode: 'open' })
}
// ✅ Good - shadow DOM with content
connectedCallback() {
this.attachShadow({ mode: 'open' })
this.shadowRoot.innerHTML = `<div>Hello</div>`
}
3. Properties Not Set
Check that properties are being set:
set label(value: string) {
console.log('Label set:', value) // Debug log
this._label = value
this.render()
}
Component Shows Error Message
Symptoms:
- Error boundary displays "Component failed to load".
- Browser console displays errors.
Solution: Use the following steps to identify the source of the error.
1. Check browser console
Press F12 and look for:
- JavaScript errors.
- Failed network requests.
- Type errors.
2. Check Network tab
Verify the bundle loaded correctly:
- Open the Network tab.
- Refresh the page.
- Filter by JS.
- Check if your bundle displays with status 200.
3. Inspect elements tab
Check if the custom element is defined:
// In browser console
console.log(customElements.get('my-component'))
// Should return constructor, not undefined
4. Test in dev harness
npm run dev
If it works in dev harness but not in Unqork:
- Check for platform API incompatibilities.
- Verify callbacks are optional chained (
?.). - Check for DOM access outside your component.
Properties and State Issues
Properties Not Updating Component
Symptoms:
- Changing settings in Module Builder has no effect.
- Component displays stale data.
Solution:
1. Missing Setters
// ❌ Bad - no setter
class MyComponent extends HTMLElement {
label: string = ''
}
// ✅ Good - implements setter
class MyComponent extends HTMLElement {
private _label: string = ''
set label(value: string) {
this._label = value
this.render()
}
}
2. Setter Doesn't Trigger Render
set label(value: string) {
this._label = value
// ❌ Missing: this.render()
}
// ✅ Good
set label(value: string) {
this._label = value
this.render() // Trigger re-render
}
3. Type Mismatch
set count(value: unknown) {
// Validate type
if (typeof value !== 'number') {
console.warn('count must be number, got:', typeof value, value)
return
}
this._count = value
this.render()
}
debug property values:
set myProp(value: unknown) {
console.log('myProp set:', value, 'type:', typeof value)
this._myProp = value
this.render()
}
Submission Data Not Updating
Symptoms:
- Calling
onSubmissionUpdatehas no effect. - Other components don't see updated data.
Solution:
1. Callback Not Wired
Check the Module Builder triggers configuration:
{
"triggers": {
"onSubmissionUpdate": {
// Should be configured
}
}
}
onSubmissionUpdate doesn't need trigger configuration—it's handled automatically.
2. Calling Incorrectly
// ❌ Bad - calling without optional chaining
this.onSubmissionUpdate(newValue) // Throws if undefined
// ✅ Good - safe optional chaining
this.onSubmissionUpdate?.(newValue)
3. Passing Undefined
// Check value before updating
if (value !== undefined) {
this.onSubmissionUpdate?.(value)
}
Debug submission updates:
handleChange(value: string) {
console.log('Updating submission:', value)
console.log('Callback exists?', !!this.onSubmissionUpdate)
this.onSubmissionUpdate?.(value)
}
Event and Callback Issues
Callbacks Not Firing
Symptoms:
- events don't trigger workflows.
- Trigger configuration has no effect.
Solution:
1. Missing Optional Chaining
// ❌ Bad - throws if callback undefined
this.onButtonClicked({ label: this._label })
// ✅ Good - safe optional chaining
this.onButtonClicked?.({ label: this._label })
2. Wrong Callback Name
Check that manifest.json and the Module Builder configuration match:
// manifest.json
{
"events": [
{
"type": "buttonClicked" // Must match property name
}
]
}
// Component
this.onButtonClicked?.({ ... }) // Must be on[EventType]
3. Trigger Not Configured
In Module Builder, verify trigger exists:
{
"triggers": {
"onButtonClicked": {
"contextId": "clickData",
"targetId": "someLogicComponent"
}
}
}
4. Timing Issue
Callbacks are set after connectedCallback. If you call them immediately, they might not exist yet:
// ❌ Bad - callback might not be set yet
connectedCallback() {
this.onReady?.() // May be undefined
}
// ✅ Good - wait for platform to wire callbacks
connectedCallback() {
setTimeout(() => {
this.onReady?.()
}, 0)
}
Debug callbacks:
handleClick() {
console.log('Callbacks available:', {
onButtonClicked: typeof this.onButtonClicked,
onSubmissionUpdate: typeof this.onSubmissionUpdate,
})
this.onButtonClicked?.({ label: this._label })
}
Build and Bundle Issues
Build Fails
Build failures usually trace to one of three issues.
1. TypeScript Errors
error TS2304: Cannot find name 'X'
Solution:
# Install missing types
npm install -D @types/X
# Or fix TypeScript errors in code
2. Module Not Found
Cannot find module './MyComponent'
Solution:
- Check that the file exists at the specified path.
- Check that the import path is correct.
- Check the file extension (
.tsvs..tsx).
3. Syntax Errors
Unexpected token ')'
Solution:
- Fix the JavaScript/TypeScript syntax.
- Check for missing closing braces, brackets, and parentheses.
Run build with verbose logging:
npm run build -- --mode development
Bundle Too Large
Symptoms:
- Bundle size > 500KB.
- Slow page loads.
- Performance warnings.
Solution:
1. Check bundle size
npm run build
ls -lh dist/*.js
2. Use code splitting
// ❌ Bad - imports everything upfront
import * as d3 from 'd3'
// ✅ Good - lazy load
async loadD3() {
const d3 = await import('d3')
return d3
}
3. Import only what you need
// ❌ Bad - imports entire library
import _ from 'lodash'
// ✅ Good - imports specific functions
import debounce from 'lodash/debounce'
import throttle from 'lodash/throttle'
4. Remove unused dependencies
npm uninstall unused-library
5. Analyze bundle
# Add to package.json
"scripts": {
"analyze": "vite build --mode analyze"
}
Validation Fails
Validation errors occur when the component manifest doesn't match the required schema.
1. Manifest Schema Invalid
manifest.json validation failed
Solution:
Check required fields in manifest.json:
{
"name": "my-component", // Required
"main": "my-component.js", // Required
"type": "custom", // Required
"productType": "BYO", // Required
"version": "1.0.0", // Required
"runtimeVersion": "1.0.0", // Required
"description": "...", // Required
"components": [] // Required (can be empty)
}
2. Export Missing
Component 'myButton' export not found in bundle
Solution:
Ensure the entry.ts exports match the manifest:
// src/entry.ts
export const myButton = { // Name must match manifest
view: async () => MyButton,
model: async () => MyButtonModel,
}
3. Missing View or Model
Component 'myButton' export is missing 'view' function
Solution:
The export must have both view and model:
export const myButton = {
view: async () => MyButton, // Required
model: async () => MyButtonModel, // Required
}
Dev Harness Issues
Dev Harness Shows Blank Page
A blank page in the dev harness typically has one of three causes.
1. Port Already in Use
# Error: Port 5173 already in use
# Use different port
npm run dev -- --port 5001
2. Build Errors
Check terminal for error messages:
# Look for compilation errors
# Fix TypeScript/JavaScript errors
3. Missing Mock Scenarios
Create at least one mock scenario:
// src/MyComponent.mocks.ts
import { defineMockScenario } from '@unqork/byo-sdk/testing'
export const defaultScenario = defineMockScenario({
name: 'Default',
props: {
label: 'Click me',
},
callbacks: ['onButtonClicked'],
})
Mock Scenarios Not Displaying
If your scenarios don't display in the drop-down, one of the following issues is usually responsible.
1. File Naming
Scenarios must be in files matching *.mocks.ts:
✓ MyComponent.mocks.ts
✓ scenarios.mocks.ts
✗ MyComponent.scenarios.ts // Wrong
✗ mocks.ts // Wrong
2. Export Format
Export scenario objects:
// ✅ Good
export const myScenario = defineMockScenario({
name: 'My Scenario',
props: { ... },
})
// ❌ Bad - not exported
const myScenario = defineMockScenario({ ... })
3. Missing Name
Scenarios need a name to display in the drop-down:
defineMockScenario({
name: 'Default', // Required
props: { ... },
})
Performance Issues
Slow Page Loads
Slow load times with BYO components usually trace to one of four causes.
1. Too Many BYO Components
Each component adds overhead. Combine related components:
❌ 10 small components = 10 bundles to load
✅ 1 combined component = 1 bundle
2. Large Bundle Size
See Bundle Too Large for steps to reduce the bundle size.
3. Synchronous Heavy Operations
Use async with batching:
async processItems(items: Item[]) {
const batchSize = 100
for (let i = 0; i < items.length; i += batchSize) {
const batch = items.slice(i, i + batchSize)
await processBatch(batch)
// Yield to browser
await new Promise(resolve => setTimeout(resolve, 0))
}
}
4. Memory Leaks
Clean up in disconnectedCallback:
private interval: number | null = null
connectedCallback() {
this.interval = setInterval(this.update, 1000)
}
disconnectedCallback() {
if (this.interval) {
clearInterval(this.interval)
}
}
Browser Compatibility Issues
Component Works in Chrome but Not Safari
Browser compatibility issues typically come from modern API usage, Shadow DOM differences, or unsupported CSS features.
1. Missing Polyfills
Check if you're using any modern APIs:
structuredClone: Use a polyfill or deep clone manually.replaceAll: Usesplit().join()instead.- Optional chaining
?.: Supported in all modern browsers.
2. Shadow DOM Differences
Test Shadow DOM behavior across browsers.
3. CSS Features
Some CSS features have browser-specific support:
- Container queries.
:has()selector.- Cascade layers.
4. Test in All Target Browsers
Test in Chrome, Firefox, Safari, and Edge.
Getting Help
Debug Checklist
Before asking for help:
- Check browser console for errors.
- Test in dev harness (
npm run dev). - Check bundle loaded in Network tab.
- Verify the custom element is defined in the console.
- Test with simple mock data.
- Check for TypeScript errors.
- Review FAQ.
Reporting Issues
When reporting bugs, include:
1. SDK Version:
npx byo version
2. Error Messages:
- Browser console errors.
- Build/validation errors.
- Stack traces.
3. Minimal Reproduction:
- Simplest code that demonstrates the problem.
- Steps to reproduce.
- Expected vs. actual behavior.
4. Environment:
- Browser and version.
- operating system.
- Node version.
Contact your Unqork account team or submit a support request through your organization's support portal.
Additional Resources
- BYO Overview — feature introduction.
- SDK Documentation — SDK reference.
- Building Components — step-by-step guide.
- Testing Guide — testing patterns.
- Best Practices — guidelines.
- FAQ — common questions.
Changelog
| Date | Change |
|---|---|
| 2026-05-01 | Initial publication |