Building Component Libraries
Inglorious Web types are uniquely suited for building component libraries because they're fully customizable through JavaScript.
The Advantage
Unlike React components (restricted by props), Inglorious types can be customized completely:
// React: Limited to exposed props
<MyTable columns={cols} data={data} onSort={...} />
// Can't customize rendering beyond what component author exposed
// Inglorious Web: Complete customization
const myTable = {
...table,
render(entity, api) {
// Override entire rendering
},
sortRows(entity, field) {
// Override sorting logic
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Publishing a Component Library
1. Export Your Types
// @mycompany/design-system/components/card.js
export const card = {
render(entity, api) {
return html`
<div class="card">
<div class="card-header">${entity.title}</div>
<div class="card-body">${entity.content}</div>
</div>
`
},
}
// @mycompany/design-system/components/button.js
export const button = {
onClick(entity, handler) {
handler()
},
render(entity, api) {
return html`
<button
class=${"btn btn-" + entity.variant}
@click=${() => api.notify(`#${entity.id}:onClick`)}
>
${entity.label}
</button>
`
},
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2. Export Behaviors
Behaviors are reusable mixins:
// @mycompany/design-system/behaviors/sortable.js
export const sortable = (type) => ({
sortBy(entity, field) {
entity.sortField = field
entity.sortDirection = entity.sortDirection === "asc" ? "desc" : "asc"
},
render(entity, api) {
const render = type.render(entity, api)
// Wrap original render with sort UI
return html` ${renderSortControls(entity, api)} ${render} `
},
})
// @mycompany/design-system/behaviors/exportable.js
export const exportable = (type) => ({
exportData(entity, format) {
const data = formatData(entity.data, format)
downloadFile(data, `export.${format}`)
},
render(entity, api) {
const render = type.render(entity, api)
return html` ${render} ${renderExportButton(entity, api)} `
},
})
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
3. Create Preset Combinations
// @mycompany/design-system/presets/basicTable.js
import { table } from "@inglorious/web/table"
import { sortable, exportable } from "./behaviors"
export const basicTable = table
export const sortableTable = [table, sortable]
export const advancedTable = [
table,
sortable,
exportable,
{
// Additional custom logic
},
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Users Customize Freely
Your library users can customize exactly what they need:
import { table, sortable, exportable } from "@mycompany/design-system"
// Use as-is
const simpleTable = { ...table }
// Override specific method
const customTable = {
...table,
render(entity, api) {
return html`
<table class="my-custom-style">
<!-- Custom rendering -->
</table>
`
},
}
// Compose multiple behaviors
const advancedTable = [
table,
sortable,
exportable,
{
// Add custom behavior
onRowClick(entity, rowId) {
entity.selectedRow = rowId
},
},
]
// Mix and match
const customAdvanced = [
{
...table,
render(entity, api) {
// Custom render
},
},
sortable,
{
// Only export, not sortable
exportData(entity, format) {
// Custom export
},
},
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
Documentation Example
# Card Component
A flexible card component for displaying grouped content.
## Basic Usage
\`\`\`javascript
import { card } from '@mycompany/design-system'
const types = {
card,
}
const entities = {
myCard: {
type: 'card',
title: 'Hello',
content: 'World',
},
}
\`\`\`
## Customization
Override the render method:
\`\`\`javascript
const customCard = {
...card,
render(entity, api) {
return html\`
<div class="my-card">
<header style="background: ${entity.headerColor}">
${entity.title}
</header>
<main>${entity.content}</main>
</div>
\`
},
}
\`\`\`
## Composition
Compose with behaviors:
\`\`\`javascript
import { card } from '@mycompany/design-system'
import { draggable, closeable } from '@mycompany/design-system/behaviors'
const types = {
modalCard: [card, draggable, closeable],
}
\`\`\`
## Testing
Test with \`trigger()\`:
\`\`\`javascript
import { card } from '@mycompany/design-system'
import { trigger } from '@inglorious/web/test'
test('card renders title', () => {
const { entity } = trigger(
{ type: 'card', title: 'Test', content: 'Content' },
() => {},
card.render
)
expect(entity.title).toBe('Test')
})
\`\`\`
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
Benefits for Library Authors
✅ Users get complete control without forking
✅ No prop interface to maintain — Just plain JavaScript
✅ Composition is clean — Arrays instead of wrapper hell
✅ Testing is trivial — No special setup needed
✅ Documentation is simple — Just show the code
Best Practices
Export both components and behaviors
javascriptexport { card, button, table } export { sortable, draggable, closeable }1
2Provide presets for common combinations
javascriptexport { advancedTable, sortableTable }1Document customization patterns
- Show how to override render
- Show how to add behaviors
- Provide examples
Keep components flexible
- Don't hide internals
- Allow full customization
- Avoid excessive logic in render
Version carefully
- Changing method signatures breaks user customizations
- Prefer adding new methods over changing existing ones
- Document breaking changes clearly
Examples
The Inglorious ecosystem includes libraries built this way:
- @inglorious/web — Form, Table, Router, etc.
- Inglorious Motion — Motion behaviors for variants, presence, and layout transitions in Inglorious Web apps
- Inglorious Charts — Declarative chart types and composition helpers for Inglorious Web
- Inglorious Engine — Game entity types
Check them out for inspiration!
Next Steps
- Type Composition — Master composition patterns
- Testing — Ensure your library works correctly
Happy building! 📦
Inglorious Web