Skip to content

Commit 63b6f8f

Browse files
authored
docs(readme): add summary and usage (#4)
1 parent 3e40847 commit 63b6f8f

2 files changed

Lines changed: 345 additions & 2 deletions

File tree

.changeset/new-rings-lie.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@devsantara-labs/head": patch
3+
---
4+
5+
docs(readme): add summary and usage

README.md

Lines changed: 340 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,341 @@
1-
# @devsantara/head
1+
# @devsantara-labs/head
22

3-
A type-safe HTML head builder
3+
A type-safe HTML head builder library for managing document head metadata with full TypeScript support.
4+
5+
> **⚠️ Note**: This library is currently under active development and may receive breaking changes. The API is subject to change as we continue to improve and expand functionality.
6+
7+
## Features
8+
9+
- 🔒 **Type-Safe**: Full TypeScript support with React HTML attribute types
10+
- 🔗 **Fluent API**: Chainable builder pattern for intuitive metadata construction
11+
- 🧩 **Adapter System**: Extensible architecture for different frameworks (e.g, React, TanStack Router)
12+
- 🛠️ **Custom Adapters**: Create your own adapters to transform output for any framework
13+
14+
## Installation
15+
16+
```bash
17+
# npm
18+
npm install @devsantara-labs/head
19+
20+
# pnpm
21+
pnpm add @devsantara-labs/head
22+
23+
# yarn
24+
yarn add @devsantara-labs/head
25+
```
26+
27+
## Quick Start
28+
29+
```typescript
30+
import { HeadBuilder } from '@devsantara-labs/head';
31+
32+
const head = new HeadBuilder()
33+
.meta({ charSet: 'utf-8' })
34+
.meta({ name: 'viewport', content: 'width=device-width, initial-scale=1' })
35+
.link({ rel: 'stylesheet', href: '/styles.css' })
36+
.build();
37+
```
38+
39+
Returns an array of HeadElement objects
40+
41+
```typescript
42+
// console.log(head);
43+
[
44+
{ type: 'meta', attributes: { charSet: 'utf-8' } },
45+
{
46+
type: 'meta',
47+
attributes: {
48+
name: 'viewport',
49+
content: 'width=device-width, initial-scale=1',
50+
},
51+
},
52+
{ type: 'link', attributes: { rel: 'stylesheet', href: '/styles.css' } },
53+
];
54+
```
55+
56+
## Usage Examples
57+
58+
### Adding Meta Elements
59+
60+
```typescript
61+
const head = new HeadBuilder()
62+
.meta({ charSet: 'utf-8' })
63+
.meta({ name: 'viewport', content: 'width=device-width, initial-scale=1' })
64+
.meta({ name: 'description', content: 'A type-safe head builder' })
65+
.build();
66+
```
67+
68+
### Adding Link Elements
69+
70+
```typescript
71+
const head = new HeadBuilder()
72+
.link({ rel: 'stylesheet', href: '/styles.css' })
73+
.link({ rel: 'icon', href: '/favicon.ico' })
74+
.link({ rel: 'canonical', href: 'https://example.com' })
75+
.build();
76+
```
77+
78+
### Adding Script Elements
79+
80+
```typescript
81+
const head = new HeadBuilder()
82+
.script({ src: '/script.js', async: true })
83+
.script({ children: 'console.log("Hello, World!");' })
84+
.build();
85+
```
86+
87+
### Adding Style Elements
88+
89+
```typescript
90+
const head = new HeadBuilder()
91+
.style({ children: 'body { margin: 0; padding: 0; }' })
92+
.build();
93+
```
94+
95+
### Complete Example
96+
97+
```typescript
98+
import { HeadBuilder } from '@devsantara-labs/head';
99+
import { HeadReactAdapter } from '@devsantara-labs/head/adapters';
100+
101+
const head = new HeadBuilder({
102+
metadataBase: new URL('https://example.com'),
103+
adapter: new HeadReactAdapter(),
104+
})
105+
.meta({ charSet: 'utf-8' })
106+
.meta({ name: 'viewport', content: 'width=device-width, initial-scale=1' })
107+
.link({ rel: 'stylesheet', href: '/styles.css' })
108+
.link({ rel: 'icon', href: '/favicon.ico' })
109+
.script({ src: '/analytics.js', async: true })
110+
.style({ children: 'body { font-family: system-ui; }' })
111+
.build();
112+
```
113+
114+
## Framework Adapters
115+
116+
### React Adapter
117+
118+
Converts head elements to React elements for rendering.
119+
120+
```typescript
121+
import { HeadBuilder } from '@devsantara-labs/head';
122+
import { HeadReactAdapter } from '@devsantara-labs/head/adapters';
123+
124+
const head = new HeadBuilder({ adapter: new HeadReactAdapter() })
125+
.meta({ charSet: 'utf-8' })
126+
.meta({ name: 'viewport', content: 'width=device-width, initial-scale=1' })
127+
.link({ rel: 'icon', href: '/favicon.ico' })
128+
.build();
129+
130+
// Use in React component
131+
function App() {
132+
return (
133+
<>
134+
<head>{head}</head>
135+
<body>...</body>
136+
</>
137+
);
138+
}
139+
```
140+
141+
**Output Type**: `ReactNode[]`
142+
143+
### TanStack Router Adapter
144+
145+
Converts head elements to TanStack Router head configuration format.
146+
147+
```typescript
148+
import { HeadBuilder } from '@devsantara-labs/head';
149+
import { HeadTanstackRouterAdapter } from '@devsantara-labs/head/adapters';
150+
151+
const head = new HeadBuilder({ adapter: new HeadTanstackRouterAdapter() })
152+
.meta({ charSet: 'utf-8' })
153+
.link({ rel: 'stylesheet', href: '/styles.css' })
154+
.script({ src: '/script.js' })
155+
.build();
156+
157+
// Use in TanStack Router route
158+
export const Route = createRootRoute({
159+
head: () => head,
160+
});
161+
```
162+
163+
**Output Type**: Object with categorized elements (compatible with TanStack Router head configuration):
164+
165+
```typescript
166+
{
167+
meta?: HeadMetaAttributes[];
168+
link?: HeadLinkAttributes[];
169+
script?: HeadScriptAttributes[];
170+
style?: HeadStyleAttributes[];
171+
}
172+
```
173+
174+
### Creating Custom Adapters
175+
176+
You can create your own adapter by implementing the `HeadAdapter<T>` interface. This allows you to transform the head elements into any format required by your framework or use case.
177+
178+
#### HeadAdapter Interface
179+
180+
```typescript
181+
import type { HeadAdapter, HeadElement } from '@devsantara-labs/head';
182+
183+
export interface HeadAdapter<T> {
184+
transform(elements: HeadElement[]): T;
185+
}
186+
```
187+
188+
The `HeadElement` type represents a single head element:
189+
190+
```typescript
191+
type HeadElement = {
192+
type: 'meta' | 'link' | 'script' | 'style';
193+
attributes:
194+
| HeadMetaAttributes
195+
| HeadLinkAttributes
196+
| HeadScriptAttributes
197+
| HeadStyleAttributes;
198+
};
199+
```
200+
201+
#### Example: Creating a Custom Adapter
202+
203+
Here's an example of creating a custom adapter that transforms head elements into plain HTML strings:
204+
205+
```typescript
206+
import type { HeadAdapter, HeadElement } from '@devsantara-labs/head';
207+
import { HeadBuilder } from '@devsantara-labs/head';
208+
209+
// Define your output type
210+
type HtmlStringOutput = string;
211+
212+
// Implement the HeadAdapter interface
213+
class HeadHtmlStringAdapter implements HeadAdapter<HtmlStringOutput> {
214+
transform(elements: HeadElement[]): HtmlStringOutput {
215+
return elements
216+
.map((element) => {
217+
const { type, attributes } = element;
218+
const attrs = Object.entries(attributes)
219+
.filter(([key]) => key !== 'children')
220+
.map(([key, value]) => `${key}="${value}"`)
221+
.join(' ');
222+
223+
const children = (attributes as { children?: string }).children || '';
224+
225+
return `<${type} ${attrs}>${children}</${type}>`;
226+
})
227+
.join('\n');
228+
}
229+
}
230+
231+
// Use your custom adapter
232+
const head = new HeadBuilder({ adapter: new HeadHtmlStringAdapter() })
233+
.meta({ charSet: 'utf-8' })
234+
.meta({ name: 'description', content: 'My awesome site' })
235+
.link({ rel: 'stylesheet', href: '/styles.css' })
236+
.build();
237+
238+
console.log(head);
239+
// Output:
240+
// <meta charSet="utf-8" />
241+
// <meta name="description" content="My awesome site" />
242+
// <link rel="stylesheet" href="/styles.css" />
243+
```
244+
245+
## API Reference
246+
247+
### HeadBuilder
248+
249+
The main class for building head elements.
250+
251+
#### Constructor
252+
253+
```typescript
254+
new HeadBuilder(options?: {
255+
metadataBase?: URL;
256+
adapter?: HeadAdapter<TOutput>;
257+
})
258+
```
259+
260+
| Option | Type | Description |
261+
| -------------- | ---------------------- | ------------------------------------------------ |
262+
| `metadataBase` | `URL` | Base URL for resolving relative URLs in metadata |
263+
| `adapter` | `HeadAdapter<TOutput>` | Optional adapter to transform build output |
264+
265+
#### Methods
266+
267+
| Method | Parameters | Returns | Description |
268+
| ------------------- | ---------------------------------- | -------------------------- | ------------------------------------------------------ |
269+
| `meta()` | `attributes: HeadMetaAttributes` | `this` | Adds a `<meta>` element |
270+
| `link()` | `attributes: HeadLinkAttributes` | `this` | Adds a `<link>` element |
271+
| `script()` | `attributes: HeadScriptAttributes` | `this` | Adds a `<script>` element |
272+
| `style()` | `attributes: HeadStyleAttributes` | `this` | Adds a `<style>` element |
273+
| `build()` | - | `TOutput \| HeadElement[]` | Returns the final output (adapted if adapter provided) |
274+
| `getMetadataBase()` | - | `URL \| undefined` | Returns the configured metadataBase URL |
275+
276+
### HeadAdapter
277+
278+
Interface for creating custom adapters that transform head elements into framework-specific formats.
279+
280+
#### Interface Definition
281+
282+
```typescript
283+
interface HeadAdapter<T> {
284+
transform(elements: HeadElement[]): T;
285+
}
286+
```
287+
288+
| Type Parameter | Description |
289+
| -------------- | --------------------------------------- |
290+
| `T` | The output type returned by the adapter |
291+
292+
#### Method
293+
294+
| Method | Parameters | Returns | Description |
295+
| ------------- | ------------------------- | ------- | --------------------------------------------------------- |
296+
| `transform()` | `elements: HeadElement[]` | `T` | Transforms an array of head elements to the target format |
297+
298+
#### Types
299+
300+
**HeadElement**
301+
302+
```typescript
303+
type HeadElement = {
304+
type: 'meta' | 'link' | 'script' | 'style';
305+
attributes:
306+
| HeadMetaAttributes
307+
| HeadLinkAttributes
308+
| HeadScriptAttributes
309+
| HeadStyleAttributes;
310+
};
311+
```
312+
313+
**Attribute Types**
314+
315+
All attribute types are based on React's `DetailedHTMLProps` for their respective HTML elements:
316+
317+
- `HeadMetaAttributes` - Attributes for `<meta>` elements
318+
- `HeadLinkAttributes` - Attributes for `<link>` elements
319+
- `HeadScriptAttributes` - Attributes for `<script>` elements
320+
- `HeadStyleAttributes` - Attributes for `<style>` elements
321+
322+
## Notes
323+
324+
### Type Safety
325+
326+
This library leverages React's built-in HTML attribute types (`DetailedHTMLProps`) to provide comprehensive type safety for all HTML head elements. This ensures you only use valid attributes for each element type and helps catch errors at compile time.
327+
328+
### Metadata Base URL
329+
330+
The `metadataBase` option allows you to configure a base URL for resolving relative URLs in your metadata. This is useful for ensuring canonical URLs and other metadata references are absolute.
331+
332+
## References
333+
334+
- [MDN: `<meta>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
335+
- [MDN: `<link>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
336+
- [MDN: `<script>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
337+
- [MDN: `<style>` element](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/style)
338+
339+
## License
340+
341+
Licensed under the [MIT license](./LICENSE).

0 commit comments

Comments
 (0)