This review covers the implementation of bio landing page blocks, which extends the bio engine with four new block types (Markdown, Image, Two-Panel, and Cart) to enable full landing page creation capabilities.
Branch: feature/bio-landing-page-blocks
Files Changed: 21 files modified, 1,162 insertions, 581 deletions
Review Date: 2025-08-31
File: packages/ui/src/bio/contexts/brand-kit-context.tsx
Lines: 37, 58, 74
Issue: Multiple type assertions (as React.CSSProperties) violate the project's strict TypeScript policy
// CURRENT - FORBIDDEN
} as React.CSSProperties;
// SOLUTION - Define proper interface
interface CSSPropertiesWithFonts extends React.CSSProperties {
fontFamily?: string;
}Impact: Breaks TypeScript safety guarantees and violates codebase standards
File: Multiple bio page components
Lines: Various (bio-image-page.tsx:373, bio-two-panel-page.tsx:414)
Issue: Type assertions for tab values without proper validation
// CURRENT - RISKY
onValueChange={value => setUploadTab(value as 'upload' | 'library')}
// SOLUTION - Proper type checking
onValueChange={value => {
if (value === 'upload' || value === 'library') {
setUploadTab(value);
}
}}File: packages/lib/src/trpc/routes/bio.route.ts
Lines: 98-101
Issue: Basic error handling without proper context or recovery
// CURRENT - INSUFFICIENT
} catch (error) {
throw new TRPCError({
code: 'INTERNAL_SERVER_ERROR',
// Missing proper error contextSolution: Add proper error context, logging, and recovery strategies
File: packages/ui/src/bio/blocks/image-block.tsx
Lines: 22-24
Issue: No validation for image file existence or format
// CURRENT - UNSAFE
if (!block.imageFile?.s3Key) {
return null; // Silent failure
}
// SOLUTION - Proper validation with error boundaries
if (!block.imageFile?.s3Key) {
return <ErrorBoundary message="Image not found" />;
}Files: All block components
Issue: Components may re-render unnecessarily due to inline object creation
// CURRENT - CREATES NEW OBJECT ON EVERY RENDER
style={{
fontFamily: computedStyles.fonts.headingFont,
}}
// SOLUTION - Memoize styles
const headingStyle = useMemo(() => ({
fontFamily: computedStyles.fonts.headingFont,
}), [computedStyles.fonts.headingFont]);File: packages/ui/src/bio/blocks/markdown-block.tsx
Issue: Importing entire MDXClient library for simple markdown rendering
Solution: Consider lighter markdown parser for simple use cases
Files: All block components
Issue: Interactive elements lack proper ARIA labels
// CURRENT - POOR ACCESSIBILITY
<button onClick={handleImageClick}>
// SOLUTION - PROPER ACCESSIBILITY
<button
onClick={handleImageClick}
aria-label="Open image in lightbox"
role="button"
>File: packages/ui/src/bio/blocks/image-block.tsx
Lines: 107-161
Issue: Lightbox modal doesn't properly manage focus
Files: All bio page components (bio-*-page.tsx)
Issue: Similar patterns repeated across components without abstraction
Examples:
- Tab management logic duplicated 4 times
- Block update mutation patterns repeated
- Form state management duplicated
Solution: Create shared hooks like:
// useBlockEditor.ts
export function useBlockEditor(blockId: string) {
// Shared logic for all block editors
}
// useBlockTabs.ts
export function useBlockTabs() {
// Shared tab management
}File: apps/app/src/app/[handle]/bio/_components/bio-blocks-page.tsx
Lines: 454-522
Issue: Business logic mixed with UI logic in component
File: packages/db/src/sql/bio.sql.ts
Issue: Multiple similar target fields in BioBlocks table
-- CURRENT - REDUNDANT
targetLinkId
targetBioId
targetFmId
targetCartFunnelId
targetUrl
-- SUGGESTION - POLYMORPHIC DESIGN
targetType: 'link' | 'bio' | 'fm' | 'cart' | 'url'
targetId: string
targetData: jsonb -- for additional target-specific dataFile: packages/ui/src/bio/contexts/bio-context.tsx
Issue: Large number of callback props passed through context
Solution: Consider using a reducer pattern or event emitter for actions
Files: All block components
Issue: No loading states during async operations
// SUGGESTION - ADD LOADING STATES
{isLoading ? (
<div className="animate-pulse bg-gray-200 h-8 w-full rounded" />
) : (
<MarkdownContent />
)}Files: All block render components
Issue: No error boundaries for failed block rendering
Files: All new components
Issue: No test files found for new block components
Recommendation: Add comprehensive tests:
// markdown-block.test.tsx
describe('MarkdownBlock', () => {
it('renders markdown content correctly', () => {
// Test implementation
});
it('handles empty markdown gracefully', () => {
// Test implementation
});
});File: packages/ui/src/bio/blocks/markdown-block.tsx
Lines: 160-162
Issue: MDX content rendered without sanitization
Solution: Implement content sanitization for user-generated markdown
File: packages/validators/src/schemas/bio.schema.ts
Lines: 180-181
Issue: Only length validation for markdown content
// SUGGESTION - ENHANCED VALIDATION
markdown: z.string()
.max(5000, 'Content too long')
.refine(content => !containsMaliciousPatterns(content), 'Invalid content')- Consistent API Design: All block types follow the same tRPC pattern
- Type-Safe Database Schema: Proper Drizzle ORM usage with relations
- Component Composition: Good separation of concerns between blocks
- Brand Kit Integration: Consistent theming across all blocks
- Intuitive Block Editor: Drag-and-drop functionality with visual feedback
- Progressive Enhancement: Graceful degradation for missing data
- Mobile-First Design: Responsive layouts for all block types
- Consistent UI Patterns: Follows established design system
- TypeScript Strictness: No TypeScript errors in build
- Consistent Naming: Following kebab-case file naming conventions
- Proper Error Propagation: Using TRPCError for API errors
- Modern React Patterns: Using Suspense, proper hooks, and context
| Category | Score | Notes |
|---|---|---|
| TypeScript Safety | 6/10 | Type assertions need fixing |
| Performance | 7/10 | Good patterns but optimization needed |
| Security | 6/10 | Input validation needs enhancement |
| Accessibility | 5/10 | Missing ARIA labels and focus management |
| Maintainability | 8/10 | Good structure but some duplication |
| Testing | 3/10 | No tests for new components |
| Overall | 6.5/10 | Solid foundation with critical fixes needed |
- Remove all type assertions from brand-kit-context.tsx
- Fix unsafe type casting in tab components
- Add proper error handling to database operations
- Implement input validation for file operations
- Add comprehensive test coverage
- Implement proper accessibility features
- Add error boundaries for block rendering
- Create shared hooks to reduce duplication
- Implement polymorphic database design
- Add content sanitization
- Optimize bundle sizes
- Add performance monitoring
This is a well-architected feature that successfully extends the bio engine with landing page capabilities. The implementation follows established patterns and provides excellent user experience. However, several critical type safety issues must be addressed before merging, and the codebase would benefit from additional testing and accessibility improvements.
The core functionality is solid and the architectural decisions are sound. With the critical fixes applied, this feature will provide significant value to users while maintaining code quality standards.
Recommendation: Fix critical issues, then proceed with merge and address warnings in subsequent iterations.
File: apps/app/src/app/[handle]/bio/design/color-customizer.tsx
Lines: 394, 422
Issue: Console error logging left in production code
// CURRENT - DEBUG CODE IN PRODUCTION
console.error('Error parsing OKLCH:', e);
console.error('Error converting color:', e);
// SOLUTION - Proper error handling
import { logger } from '@barely/utils';
logger.error('Color parsing failed', { error: e, context: 'OKLCH parsing' });File: apps/app/src/app/[handle]/bio/_components/bio-two-panel-page.tsx
Line: 19
Issue: Commented out import with TODO for AssetSelector refactoring
File: apps/bio/src/app/[handle]/email-capture-widget.tsx
Line: 161
Issue: TODO comment about missing privacy policy link
Recommendation: These TODOs should be tracked as technical debt items and addressed in upcoming sprints.
- MDXClient: Heavy dependency for markdown rendering
- DnD Kit: Large drag-and-drop library loaded for admin interface
- Recommendation: Code splitting for admin-only components
- Positive: Proper use of database indexes for new tables
- Positive: Efficient joins in bio block queries
- Concern: N+1 query potential in block rendering (mitigated by proper relations)
- Loading States: Missing for async operations
- Image Optimization: Good use of blur data URLs and lazy loading
- Memory Usage: Potential memory leaks in lightbox modal (no cleanup)
- Strong: Zod validation for all API inputs
- Concern: Markdown content not sanitized before rendering
- Missing: File type validation beyond extension checking
- Risk: MDXClient renders user content without sanitization
- Mitigation Needed: Content Security Policy headers
- Recommendation: Implement DOMPurify or similar sanitization
- Good: No sensitive data in client-side code
- Missing: Audit logs for block creation/modification
- Recommendation: Add audit trail for compliance