Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/neg4n/next-api-og-image

:bowtie: Easy way to generate open-graph images dynamically in HTML or React using Next.js API Routes. Suitable for serverless environment.
https://github.com/neg4n/next-api-og-image

hacktoberfest html html-templates html5 jsx jsx-syntax next nextjs nextjs-api-routes nextjs-middleware nextjs-plugin nextjs-serverless react react-hooks reactjs serverless typescript vercel

Last synced: 10 days ago
JSON representation

:bowtie: Easy way to generate open-graph images dynamically in HTML or React using Next.js API Routes. Suitable for serverless environment.

Awesome Lists containing this project

README

        

# Next.js API OG Image ยท [![version](https://badgen.net/npm/v/next-api-og-image)](https://www.npmjs.com/package/next-api-og-image) [![types](https://badgen.net/npm/types/next-api-og-image)](https://www.npmjs.com/package/next-api-og-image) [![license](https://badgen.net/npm/license/next-api-og-image)](https://github.com/neg4n/next-api-og-image/blob/main/LICENSE)

Simple library with purpose of providing easy way to dynamically
generate open-graph images using [Next.js API routes][next-api-routes].

If you're not familiar with dynamic open-graph images concept - please see [vercel/og-image repository's README][vercel-og-image] for very detailed explaination.

_you can treat this project as simpler and configurable version of mentioned earlier [vercel][vercel] repository_

## Features

- [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] ๐Ÿ“ฌ Multiple strategies - pass values by GET and query params or POST and JSON body
- [x] ๐Ÿฅท TypeScript compatible

## Installing

In your [Next.js][next-homepage] project, execute:

```sh
npm i next-api-og-image chrome-aws-lambda
# or
yarn add next-api-og-image chrome-aws-lambda
```

### Short note about the peer dependencies

> โ„น๏ธ If your serverless function does not fit in the allowed size frames on [Vercel][vercel] **(50MB)**, you may want to install older versions of `chrome-aws-lambda`

In order to do so, replace `chrome-aws-lambda` _(while adding the dependencies)_ with `[email protected]` **(47.6 MB)**

Please, refer to https://github.com/neg4n/next-api-og-image/issues/23#issuecomment-1090319079 for more info ๐Ÿ™

## Examples

You can find more examples here:

- JavaScript
- [Basic usage with JavaScript][basic]
- [Basic usage with Styled Components][basic-styled]
- [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]
- [Basic usage with TypeScript and React using query strategy][basic-typescript-react-query]
- [Basic usage with TypeScript and React using body strategy][basic-typescript-react-json] (JSON)
- [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 example && npm i && npm run dev` then navigate to http://localhost:3000/_

## Basic usage and explaination

##### HTML template

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

export default withOGImage({ template: { html: ({ myQueryParam }) => `

${myQueryParam}

` } })
```

##### React template

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

export default withOGImage({ template: { react: ({ myQueryParam }) =>

{myQueryParam}

} })
```

## Creating template

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` 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**

#### Examples

##### HTML template

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

export default withOGImage({ template: { html: ({ myQueryParam }) => `

${myQueryParam}

` } })
```

##### React template

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

export default withOGImage({ template: { react: ({ myQueryParam }) =>

{myQueryParam}

} })
```

_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'_

### Strategies

`next-api-og-image` allows you to choose strategy for providing values to the template. The available strategies are:

1. `query` _(default)_ - values are passed by query params and GET HTTP request.
**These values โ›”๏ธ cannot be nested nor accessed by nested destructuring in template provider function**.

2. `body` - values are passed by POST HTTP request and JSON body.
These values โœ… can be nested and accessed by nested destructuring in template provider function.

The strategies are determined by `strategy` prop in the configuration. Default strategy is `query`.

> **โš ๏ธ NOTE**
> Regardless of the strategy - all properties (every single one)
> **are implicitly casted to string, even very long JSON's nested values**

#### Types in template provider function

If you're using TypeScript, you probably want to have these things
typed. Well... its actually super easy! Simply add generic types to `withOGImage` function.

1. typed `query` strategy with query params `?foo=hello&bar=friend` will look like this:
```js
export default withOGImage<'query', 'foo' | 'bar'>(/* ... */)
```
2. typed `body` strategy with JSON payload `{ "foo": "hello", "imNested": { "bar": "friend" }}` will look like this:
```js
export default withOGImage<'body', { foo: string, imNested: { bar: string } }>({ strategy: 'body', /* ... */ })
```

#### Errors

When strategy is set to `query` and you're sending POST HTTP request with JSON body or when strategy is set to `body` and you're sending GET HTTP request with query params - `next-api-og-image` will:

1. Will throw an runtime error
2. Set appropiate response message to the client
You can disable this behaviour by setting `dev: { errorsInResponse: false }` in the configuration

### Hooking the post-generate process

In some scenarios you may want to do something _(in other words - execute some logic)_ **after generation of the image**.
This can be easily done by providing function to `hook` configuration property. The only parameter is `NextApiRequest` object with `image` attached to it.

example (JavaScript):

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

export default withOGImage({
template: {
react: ({ myQueryParam }) =>

๐Ÿ”ฅ {myQueryParam}
,
},
dev: {
inspectHtml: false,
},
hook: (innerRequest) => {
console.log(innerRequest.image)
// will print the generated image on the server as Buffer
},
})
```

### 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` 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 }) {
return `

${myQueryParam}

`
}
```

...or in TypeScript

```ts
import type { NextApiOgImageQuery } from 'next-api-og-image'

type QueryParams = 'myQueryParam'
export default function myTemplate({ myQueryParam }: Record) {
return `

${myQueryParam}

`
}
```

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` 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 template.html or template.react prop)_:

```js
const nextApiOgImageConfig = {
// Values passing strategy
strategy: 'query',
// Response's 'Content-Type' HTTP header and browser screenshot type.
type: 'png',
// Screenshot's quality. WORKS ONLY IF 'type' IS SET TO 'jpeg'
quality: 90,
// Width of the image in pixels
width: 1200,
// Height of the image in pixels
height: 630,
// 'Cache-Control' HTTP header
cacheControl: 'max-age 3600, must-revalidate',
// Hook function that allows to intercept inner NextApiRequest with `ogImage` prop attached.
// useful for e.g. saving image in the database after the generation.
// The hook function return is Map containing custom headers that will be set BEFORE sending
// response to the client.
hook: null,
// NOTE: Options within 'chrome' object only works when next-api-og-image is run in server (not serverless!!) environment.
chrome: {
// Custom command-line args passed to the browser start command
// by default, no arguments are provided.
args: null,
// Custom executable provided. Useful when you e.g. have to run Chromium instead of Google Chrome
// by default, executable is retrieved automatically (it looks for Google Chrome in the filesystem)
executable: null,
}
// NOTE: Options within 'dev' object works only when process.env.NODE_ENV === 'development'
dev: {
// Whether to replace binary data (image/screenshot) with HTML
// that can be debugged in Developer Tools
inspectHtml: true,
// Whether to set error message in response
// if there are strategy related errors
errorsInResponse: true,
},
}
```

## License

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
[vercel]: https://vercel.com/
[vercel-og-image]: https://github.com/vercel/og-image#readme
[next-api-routes]: https://nextjs.org/docs/api-routes/introduction
[content-type]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type
[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-styled]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-styled-components.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
[basic-typescript-react-query]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-typescript-react-query.tsx
[basic-typescript-react-json]: https://github.com/neg4n/next-api-og-image/tree/main/example/pages/api/basic-typescript-react-json.tsx