Skip to main content

Component Architecture

Philosophy

This guide defines a component architecture based on the Lego philosophy: components that work perfectly in default mode, but can be disassembled and reassembled in any form to meet specific design and development needs.

Core Principles

  1. Total Flexibility: Never restrict the designer or developer
  2. Substitutability: All parts must be replaceable
  3. Single Responsibility: Each subcomponent has a specific function
  4. Configurability: Order and behavior must be configurable
  5. Ease of Use: Work perfectly "out of the box"

File Structure

ComponentName/
├── index.tsx # Main component and exports
├── context.ts # Context and custom hook
├── types.ts # All typings
├── styles.ts # Style/composition definitions
└── components/
├── Content.tsx # Default content rendering
├── SubComponent1.tsx # Specific subcomponents
├── SubComponent2.tsx
└── ...

1. Context Pattern

Context Creation

// context.ts
import { createContext, useContext } from 'react'
import { ComponentCtxValue } from './types'

export const ComponentContext = createContext({} as ComponentCtxValue)

export const useComponentContext = <T = ComponentCtxValue>(
providedProps: Partial<T> = {}
) => {
const ctx = useContext(ComponentContext)

if (!ctx) {
throw new Error(
'[ComponentName] useComponentContext must be used within a ComponentName component.'
)
}

return {
...ctx,
...providedProps,
}
}

Benefits:

  • ✅ Eliminates prop drilling
  • ✅ Allows prop override in subcomponents
  • ✅ Maintains data consistency
  • ✅ Facilitates maintenance

2. Structured Typing

Type Definitions

// types.ts
export type ComponentProps = {
// Main props
title?: string
description?: string
loading?: boolean

// Configuration
order?: Array<'part1' | 'part2' | 'part3'>

// Customization
children?: ReactNode
style?: StyledProp<ComponentComposition>

// Behavior
onAction?: AnyFunction
}

// Specific props for subcomponents
export type SubComponent1Props = Pick<ComponentProps, 'title' | 'description'>
export type SubComponent2Props = Pick<ComponentProps, 'onAction'> &
Omit<OtherComponentProps, 'style' | 'debugName'>

// Context value
export type ComponentCtxValue = Omit<ComponentProps, 'style' | 'children'> & {
styles: StyleRecord<ComponentComposition>
}

3. Main Component

Index Structure

// index.tsx
export const ComponentName = (props: ComponentProps) => {
const {
children,
style,
...contextValue
} = {
...ComponentName.defaultProps,
...props,
}

const styles = useStylesFor(ComponentName.styleRegistryName, style)

return (
<ComponentContext.Provider value={{ ...contextValue, styles }}>
<ComponentContent>
{children}
</ComponentContent>
</ComponentContext.Provider>
)
}

// Static configuration
ComponentName.styleRegistryName = 'ComponentName'
ComponentName.elements = ['wrapper', 'title', 'description', 'button']
ComponentName.rootElement = 'wrapper'

// Attach subcomponents
ComponentName.SubComponent1 = SubComponent1
ComponentName.SubComponent2 = SubComponent2

// Variant types support
ComponentName.withVariantTypes = <S extends AnyRecord>(styles: S) => {
return ComponentName as ComponentWithVariants<S>
}

// Default props
ComponentName.defaultProps = {
order: ['part1', 'part2', 'part3']
} as Partial<ComponentProps>

// Style system registration
StyleRegistry.registerComponent(ComponentName)

4. Content Pattern

Intelligent Content Rendering

// components/Content.tsx
const orderMap: Record<ComponentProps['order'][0], any> = {
part1: SubComponent1,
part2: SubComponent2,
part3: SubComponent3,
}

export const ComponentContent = ({ children }: React.PropsWithChildren) => {
const { styles, order } = useComponentContext()

// Custom content takes precedence
if (children) {
return <View style={styles.wrapper}>{children}</View>
}

// Default rendering based on order
return (
<View style={styles.wrapper}>
{order.map(componentKey => {
const Component = orderMap[componentKey]

if (!Component) {
logger.warn(`Component "${componentKey}" not found in orderMap`)
return null
}

return <Component key={`${componentKey}-${Date.now()}`} />
})}
</View>
)
}

5. Subcomponents

Subcomponent Pattern

// components/SubComponent1.tsx
export const SubComponent1 = (props: SubComponent1Props) => {
const { title, description, styles, ...contextProps } = useComponentContext(props)

// Conditional rendering
if (!title && !description) return null

return (
<>
{title && <Text text={title} style={styles.title} />}
{description && <Text text={description} style={styles.description} />}
</>
)
}

Subcomponent Characteristics:

  • ✅ Receive props via context + local override
  • ✅ Intelligent conditional rendering
  • ✅ Single, well-defined responsibility
  • ✅ Can be used independently

6. Usage Patterns

Basic Usage

<ComponentName
title="Title"
description="Description"
onAction={() => {}}
/>

Order Customization

<ComponentName
title="Title"
order={['part2', 'part1', 'part3']}
/>

Complete Replacement

<ComponentName>
<CustomHeader />
<ComponentName.SubComponent2 />
<CustomFooter />
</ComponentName>

Hybrid Usage

<ComponentName title="Title">
<ComponentName.SubComponent1 description="Override" />
<CustomComponent />
<ComponentName.SubComponent3 />
</ComponentName>

7. Implementation Checklist

✅ Basic Structure

  • Context created with custom hook
  • Types defined for all components
  • Main component with Provider
  • Style system configured

✅ Core Functionality

  • Content component with intelligent rendering
  • Subcomponents with single responsibilities
  • Default props defined

✅ Flexibility

  • Children support implemented
  • Props override in subcomponents
  • Special states handled
  • Conditional rendering in all subcomponents

✅ DX (Developer Experience)

  • Error handling with clear messages
  • Debug logging
  • Complete TypeScript
  • Usage documentation

This architecture transforms components into true "Digital Legos" - pieces that fit together perfectly by default, but can be rearranged in infinite ways to create unique experiences.