Search
Loading...
Skip to content

Pre-Export Validation

Validate your design before export by detecting elements outside the page, protruding content, obscured text, and other issues that could affect the final output quality in headless environments.

10 mins
estimated time
Download
StackBlitz
GitHub

Pre-export validation catches layout and quality issues before export, preventing problems like cropped content, hidden text, and elements missing from the final output. Production-quality designs require elements to be properly positioned within the page boundaries.

This guide demonstrates how to detect elements outside the page, find protruding content, identify obscured text, and integrate validation into the export workflow in headless Node.js environments.

Getting Element Bounds#

To detect spatial issues, we need to get the bounding box of elements in global coordinates. The getGlobalBoundingBox* methods return positions that account for all transformations:

const x = engine.block.getGlobalBoundingBoxX(blockId);
const y = engine.block.getGlobalBoundingBoxY(blockId);
const width = engine.block.getGlobalBoundingBoxWidth(blockId);
const height = engine.block.getGlobalBoundingBoxHeight(blockId);
return [x, y, x + width, y + height];

This returns coordinates as [x1, y1, x2, y2] representing the top-left and bottom-right corners of the element. The overlap between element and page bounds is calculated as the intersection area divided by the element’s total area. An overlap of 0 means completely outside, while 1 (100%) means fully inside.

Detecting Elements Outside the Page#

Elements completely outside the page won’t appear in the exported output. We find these by checking for blocks with zero overlap with the page bounds:

const blockBounds = getBoundingBox(engine, blockId);
const overlap = calculateOverlap(blockBounds, pageBounds);
if (overlap === 0) {
// Element is completely outside the page

These issues are categorized as errors because the content is completely missing from the export.

Detecting Protruding Elements#

Elements that extend beyond the page boundaries will be partially cropped in the export. For each block, compare its bounds against the page bounds and calculate the overlap ratio:

// Compare element bounds against page bounds
const blockBounds = getBoundingBox(engine, blockId);
const overlap = calculateOverlap(blockBounds, pageBounds);
// Protruding: partially inside (overlap > 0) but not fully inside (overlap < 1)
if (overlap > 0 && overlap < 0.99) {

An overlap between 0% and 100% indicates the element is partially inside the page. These issues are warnings because the content is partially visible but may not appear as intended.

Finding Obscured Text#

Text hidden behind other elements may be unreadable in the final export. First, get the stacking order and all text blocks:

const children = engine.block.getChildren(page);
const textBlocks = engine.block.findByType('text');

The getChildren() method returns blocks in stacking order - elements later in the array are rendered on top. For each text block, check if any non-text element above it overlaps with its bounds:

// Elements later in children array are rendered on top
const blocksAbove = children.slice(textIndex + 1);
for (const aboveId of blocksAbove) {
// Skip text blocks - they don't typically obscure other text
if (engine.block.getType(aboveId) === '//ly.img.ubq/text') continue;
const overlap = calculateOverlap(
getBoundingBox(engine, textId),
getBoundingBox(engine, aboveId)
);
if (overlap > 0) {
// Text is obscured by element above it

We skip text-on-text comparisons since transparent text backgrounds don’t typically obscure other text. When overlap is detected, we flag the text as potentially obscured.

Checking Placeholder Content#

Placeholders mark areas where users must add content before export. First, find all placeholder blocks in the design:

const placeholders = engine.block.findAllPlaceholders();

Then inspect each placeholder’s fill to determine if content has been added. Get the fill block and check its type to determine the validation logic:

const fillId = engine.block.getFill(blockId);
if (!fillId || !engine.block.isValid(fillId)) return false;
const fillType = engine.block.getType(fillId);
// Check image fill - empty URI means unfilled placeholder
if (fillType === '//ly.img.ubq/fill/image') {
const imageUri = engine.block.getString(fillId, 'fill/image/imageFileURI');
return imageUri !== '' && imageUri !== undefined;
}

For image placeholders, check if the fill/image/imageFileURI property has a value. An empty or undefined URI indicates the placeholder hasn’t been filled. Unfilled placeholders are treated as errors that block export, ensuring users complete all required content before exporting.

Integrating with Export#

In headless environments, run validation before export and handle results programmatically. Errors block the export entirely, while warnings can be logged but allow the export to proceed:

// Validate design before export
const result = validateDesign(engine);
console.log('=== Pre-Export Validation ===');
// Log all issues for debugging
if (result.errors.length > 0) {
console.error(`Found ${result.errors.length} error(s):`);
result.errors.forEach((err) =>
console.error(` - ${err.blockName}: ${err.message}`)
);
}
if (result.warnings.length > 0) {
console.warn(`Found ${result.warnings.length} warning(s):`);
result.warnings.forEach((warn) =>
console.warn(` - ${warn.blockName}: ${warn.message}`)
);
}
// Block export for errors
if (result.errors.length > 0) {
console.error('\nExport blocked: Fix errors before exporting');
process.exit(1);
}
// Allow export with warnings
if (result.warnings.length > 0) {
console.log('\nProceeding with export despite warnings...');
} else {
console.log('\nValidation passed - no issues found');
}
// Export the design
const outputDir = './output';
if (!existsSync(outputDir)) mkdirSync(outputDir, { recursive: true });
const blob = await engine.block.export(page, { mimeType: 'image/png' });
const buffer = Buffer.from(await blob.arrayBuffer());
writeFileSync(`${outputDir}/validated-design.png`, buffer);
console.log('Export successful: output/validated-design.png');

When validation fails, log the issues and exit with an error code so calling systems know the export was blocked.

API Reference#

MethodPurpose
engine.block.getGlobalBoundingBoxX(id)Get element’s global X position
engine.block.getGlobalBoundingBoxY(id)Get element’s global Y position
engine.block.getGlobalBoundingBoxWidth(id)Get element’s global width
engine.block.getGlobalBoundingBoxHeight(id)Get element’s global height
engine.block.findByType(type)Find all blocks of a specific type
engine.block.getChildren(id)Get child blocks in stacking order
engine.block.getType(id)Get the block’s type string
engine.block.getName(id)Get the block’s display name
engine.block.isValid(id)Check if block exists
engine.block.findAllPlaceholders()Find all placeholder blocks
engine.block.getFill(id)Get the fill block
engine.block.getString(id, property)Get a string property value