Skip to content

Commit

Permalink
Merge pull request #12 from neg4n/react-template
Browse files Browse the repository at this point in the history
Create ability to provide React template
  • Loading branch information
neg4n authored Oct 14, 2021
2 parents 890a12e + 5c18463 commit 905d096
Show file tree
Hide file tree
Showing 9 changed files with 237 additions and 34 deletions.
53 changes: 44 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ _you can treat this project as simpler and configurable version of mentioned ear

- [x] 🐄 Super easy usage
- [x] 🌐 Suitable for [serverless][vercel-serverless] environment
- [x] :bowtie: Elegant way for defining templates both in [React][react] and [HTML][html]
- [x] 🥷 TypeScript compatible

## Installing
Expand All @@ -23,35 +24,63 @@ yarn add next-api-og-image

## Basic usage and explaination

##### HTML template

```js
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` } })
```

##### React template

```js
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` })
export default withOGImage({ template: { react: ({ myQueryParam }) => <h1>{myQueryParam}</h1> } })
```

## Creating template

You've may noticed the `html` property in configuration. Its responsibility is to provide HTML document to image creator _(browser screenshot)_, filled with your values.
You've may noticed the `html` and `react` properties in configuration. Their responsibility is to provide HTML document to image creator _(browser screenshot)_, filled with your values.

> **⚠️ NOTE**
> Template **cannot be ambigious**. You must either
> define `react` or `html`. Never both at once
### Specification

The `html` property is a function _(**it can be asynchronous**)_ which first (and only) parameter is nothing else but [HTTP request's query params][query-params] converted to object notation.
The `html` and `react` properties are template providers functions. Each function's first (and only) parameter is nothing else but [HTTP request's query params][query-params] converted to object notation.

This allows you to create fully customized HTML templates by simply accessing these parameters. The preferred way to do that is [object destructuring][object-destructuring].

> **⚠️ NOTE**
> `html` and `react` template provider functions
> **can be defined as asynchronous**
#### Example

##### HTML template

```js
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` })
export default withOGImage({ template: { html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>` } })
```

##### React template

```js
import { withOGImage } from 'next-api-og-image'

export default withOGImage({ template: { react: ({ myQueryParam }) => <h1>{myQueryParam}</h1> } })
```

_if you send GET HTTP request to [api route][next-api-routes] with code presented above e.g. `localhost:3000/api/foo?myQueryParam=hello` - it will render heading with content equal to 'hello'_

### Splitting files

Keeping all the templates inline within [Next.js API route][next-api-routes] should not be problematic, but if you prefer keeping things in separate files you can follow the common pattern of creating files like `my-template.html.js` with code e.g.
Keeping all the templates inline within [Next.js API route][next-api-routes] should not be problematic, but if you prefer keeping things in separate files you can follow the common pattern of creating files like `my-template.html.js` or `my-template.js` when you define template as react *(naming convention is fully up to you)* with code e.g.

```js
export default function myTemplate({ myQueryParam }) {
Expand All @@ -69,18 +98,18 @@ export default function myTemplate({ myQueryParam }: NextApiOgImageQuery<QueryPa
return `<h1>${myQueryParam}</h1>`
}
```
then importing it and embedding in the `withOGImage`.

then importing it and embedding in the `withOGImage`.

### Loading custom local fonts

In order to load custom fonts from the project source, you need to create source file with your font in **base64** format or simply bind the font file content to the variable in your [Next.js API route][next-api-routes]

## Configuration

Apart from `html` configuration property _(which is required)_, you can specify additional info about how `next-api-og-image` should behave.
Apart from `html` and `react` configuration property (in `template`) _(whose are required)_, you can specify additional info about how `next-api-og-image` should behave.

Example configuration with **default values** _(apart from required html prop)_:
Example configuration with **default values** _(apart from template.html or template.react prop)_:

```js
const nextApiOgImageConfig = {
Expand All @@ -95,7 +124,7 @@ const nextApiOgImageConfig = {
inspectHtml: true,
},
}
````
```

## Examples

Expand All @@ -104,9 +133,11 @@ You can find more examples here:
- JavaScript
- [Basic usage with JavaScript][basic]
- [Basic usage with TailwindCSS][basic-tailwind]
- [Basic usage with React template provided][basic-react]
- [Basic usage with loading custom local fonts][basic-fonts-local]
- TypeScript
- [Basic usage with TypeScript][basic-typescript]
- [Advanced usage with TypeScript, React template and custom local fonts][advanced-typescript-react]

_the `example/` directory contains simple [Next.js][next-homepage] application implementing `next-api-og-image` . To fully explore examples implemented in it by yourself - simply do `npm link && cd examples && npm i && npm run dev` then navigate to http://localhost:3000/_

Expand All @@ -116,6 +147,8 @@ This project is licensed under the MIT license.
All contributions are welcome.

[next-homepage]: https://nextjs.org/
[react]: https://reactjs.org
[html]: https://en.wikipedia.org/wiki/HTML
[object-destructuring]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#object_destructuring
[query-params]: https://en.wikipedia.org/wiki/Query_string
[vercel-serverless]: https://vercel.com/docs/concepts/functions/introduction
Expand All @@ -126,5 +159,7 @@ All contributions are welcome.
[cache-control]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
[basic-typescript]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-typescript.ts
[basic-tailwind]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-tailwind.js
[basic-react]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-react.js
[basic]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic.js
[basic-fonts-local]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-custom-fonts-local.js
[advanced-typescript-react]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/advanced-typescript-react.tsx
56 changes: 56 additions & 0 deletions example/pages/api/advanced-typescript-react.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { withOGImage } from 'next-api-og-image'
import { interRegular } from '../../fonts/inter'

const style = `
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: normal;
src: url(data:font/woff2;charset=utf-8;base64,${interRegular}) format('woff2');
}
body {
font-family: 'Inter', sans-serif;
}
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
`

export default withOGImage<'testQueryParam'>({
template: {
react: async ({ testQueryParam }) => {
const value = await someLongRunningValueGetter()
return (
<html>
<head>
<style dangerouslySetInnerHTML={{ __html: style }} />
</head>
<body>
<div className="container">
<h1>{testQueryParam}</h1>
<h2>{value}</h2>
</div>
</body>
</html>
)
},
},
dev: {
inspectHtml: false,
},
})

function someLongRunningValueGetter() {
return new Promise((resolve: (value: string) => void) => {
setTimeout(() => {
resolve("Value in setTimeout's (500ms) callback")
}, 500)
})
}
7 changes: 7 additions & 0 deletions example/pages/api/basic-react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { withOGImage } from 'next-api-og-image'

export default withOGImage({
template: {
react: ({ myQueryParam }) => <div>🔥 {myQueryParam}</div>,
},
})
22 changes: 12 additions & 10 deletions example/pages/api/basic-tailwind.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { withOGImage } from 'next-api-og-image'

export default withOGImage({
html: ({ myQueryParam }) => `
<html>
<head>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<h1 class="flex flex-row text-6xl text-blue-300">${myQueryParam}<span>💻</span></h1>
</body>
</html>
`,
template: {
html: ({ myQueryParam }) => `
<html>
<head>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet">
</head>
<body>
<h1 class="flex flex-row text-6xl text-blue-300">${myQueryParam}<span>💻</span></h1>
</body>
</html>
`,
},
})
18 changes: 10 additions & 8 deletions example/pages/api/basic-typescript.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { withOGImage } from 'next-api-og-image'

export default withOGImage<'testQueryParam'>({
html: async ({ testQueryParam }) => {
return `
<html>
<body>
<h1>${testQueryParam}</h1>
</body>
</html>
`
template: {
html: async ({ testQueryParam }) => {
return `
<html>
<body>
<h1>${testQueryParam}</h1>
</body>
</html>
`
},
},
})
4 changes: 3 additions & 1 deletion example/pages/api/basic.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { withOGImage } from 'next-api-og-image'

export default withOGImage({
html: ({ myQueryParam }) => `<h1>${myQueryParam}</h1>`,
template: {
react: ({ myQueryParam }) => <div>🔥 {myQueryParam}</div>,
},
})
78 changes: 78 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
},
"devDependencies": {
"@types/node": "^16.10.2",
"@types/react": "^17.0.29",
"@types/react-dom": "^17.0.9",
"@types/twemoji": "^12.1.2",
"microbundle": "^0.13.3",
"prettier": "^2.4.1",
Expand Down
Loading

0 comments on commit 905d096

Please sign in to comment.