Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add CldOgImage component #60

Merged
merged 9 commits into from
Nov 1, 2024
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ Once installed and configured, from the root of your project run:
pnpm dev
```

The project will now be available at <https://localhost:4321> or the configured local port.
The project will now be available at <http://localhost:4321> or the configured local port.


## Contributors
Expand Down
52 changes: 52 additions & 0 deletions astro-cloudinary/src/components/CldOgImage.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
import type { CldImageProps } from "./CldImage.astro";
import { getCldOgImageUrl } from "../helpers/getCldOgImageUrl";
import { OG_IMAGE_WIDTH, OG_IMAGE_HEIGHT } from "../constants/sizes";

export type CldOgImageProps = CldImageProps & {
twitterTitle: string;
};

interface Props extends CldOgImageProps {}

const { twitterTitle, alt, ...props }: Props = Astro.props;

// check if the twitter title exists
if (!twitterTitle) {
throw new Error("twitterTitle is required for the CldOgImage component");
}

// We need to separately handle the width and the height to allow our user to pass in
// a custom value, but also we need to know this at the component level so that we can
// use it when rendering the meta tags
let { width = OG_IMAGE_WIDTH, height = OG_IMAGE_HEIGHT } = props;

// Normalize the width and height
width = typeof width === "string" ? Number.parseInt(width) : width;
height = typeof height === "string" ? Number.parseInt(height) : height;

// Render the final URLs. We use two different format versions to deliver
// webp for Twitter as it supports it (and we can control with tags) where
// other platforms may not support webp, so we deliver jpg
const ogImageUrl = getCldOgImageUrl({
...Astro.props,
width,
height,
});

const twitterImageUrl = getCldOgImageUrl({
...Astro.props,
width,
height,
format: Astro.props.format || "webp",
});
---

<meta property="og:image" content={ogImageUrl} />
<meta property="og:image:secure_url" content={ogImageUrl} />
<meta property="og:image:width" content={String(OG_IMAGE_WIDTH)} />
<meta property="og:image:height" content={String(OG_IMAGE_HEIGHT)} />
{alt && <meta property="og:image:alt" content={alt} />}
<meta property="twitter:title" content={twitterTitle} />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:image" content={twitterImageUrl} />
5 changes: 5 additions & 0 deletions astro-cloudinary/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ export {
type CldImageProps
} from './CldImage.astro';

export {
default as CldOgImage,
type CldOgImageProps
} from './CldOgImage.astro';

export {
default as CldVideoPlayer,
type CldVideoPlayerProps,
Expand Down
13 changes: 13 additions & 0 deletions docs/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,19 @@ export default defineConfig({
}
]
},
{
label: 'CldOgImage',
items: [
{
label: 'Basic Usage',
slug: 'cldogimage/basic-usage'
},
{
label: 'Configuration',
slug: 'cldogimage/configuration'
}
]
},
{
label: 'CldUploadWidget',
items: [
Expand Down
46 changes: 19 additions & 27 deletions docs/src/components/Head.astro
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
---
import type { Props } from '@astrojs/starlight/props'
import Default from '@astrojs/starlight/components/Head.astro'
import { getCldOgImageUrl } from '../../../astro-cloudinary/helpers';

// Get the URL of the generated image for the current page using its
// ID and replace the file extension with `.png`.
import { CldOgImage } from '../../../astro-cloudinary';

const { slug, entry } = Astro.props;
const { title, ogImageTitle } = entry.data;
---
<Default {...Astro.props}><slot /></Default>

let ogImageUrl;

if ( slug === '' ) {
ogImageUrl = getCldOgImageUrl({
src: `${import.meta.env.PUBLIC_ASSETS_DIRECTORY}/astro-cloudinary-social-card-1.1`
});
} else {
ogImageUrl = getCldOgImageUrl({
src: `${import.meta.env.PUBLIC_ASSETS_DIRECTORY}/astro-cloudinary-social-card-template-1.1`,
overlays: [
{slug === '' && (
<CldOgImage
src={`${import.meta.env.PUBLIC_ASSETS_DIRECTORY}/astro-cloudinary-social-card-1.1`}
alt={ogImageTitle || title}
twitterTitle={title}
/>
)}
{slug !== '' && (
<CldOgImage
src={`${import.meta.env.PUBLIC_ASSETS_DIRECTORY}/astro-cloudinary-social-card-template-1.1`}
overlays={[
{
width: 1000,
crop: 'fit',
Expand Down Expand Up @@ -53,16 +53,8 @@ if ( slug === '' ) {
lineSpacing: -25
}
},
]
});
}
---
<Default {...Astro.props}><slot /></Default>

<meta property="og:image" content={ogImageUrl} />
<meta property="og:image:secure_url" content={ogImageUrl} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="twitter:title" content={title} />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:image" content={ogImageUrl} />
]}
alt={ogImageTitle || title}
twitterTitle={title}
/>
)}
166 changes: 166 additions & 0 deletions docs/src/content/docs/cldogimage/basic-usage.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
title: Getting Started with CldOgImage
description: Auto generate OG images and the relevant tags in Astro with the CldOgImage component.
ogImageTitle: CldOgImage
head:
- tag: title
content: CldOgImage - Astro Cloudinary
---
import { CldImage } from '../../../../../astro-cloudinary';

import CodeBlock from '../../../components/CodeBlock.astro';
import HeaderImage from '../../../components/HeaderImage.astro';
import DemoImageEvents from '../../../components/DemoImageEvents.astro';

The CldOgImage component provides an easy way to deliver images from Cloudinary in an Astro app.

With it comes access to more advanced features like dynamic cropping, background removal, overlays, and other Cloudinary transformations.

As CldImage is a wrapper around the Unpic Image component, you also gain access to built-in Image component features that will work out-of-the-box like [Responsive Sizing](/guides/responsive-images).

## Basic Usage

The basic required props include `twitterTitle`, `src`, and `alt`:

<HeaderImage>
<CldImage
src="cld-sample-4"
width="1870"
height="1250"
alt="A variety of colorful and appetizing breakfast dishes, including waffles, oatmeal, and fresh fruits, are arranged on a white surface, accompanied by various condiments and utensils."
sizes="100vw"
/>
</HeaderImage>

> CldOgImage does not render an `<img>` tag, meaning it can't be visually embedded on a page. The following examples make use of the `<CldImage>` tag to showcase what's possible.

<CodeBlock alwaysExpanded>
```jsx
---
import { CldOgImage } from 'astro-cloudinary';
---
<CldOgImage
src="<Public ID>"
twitterTitle="<Title>"
alt="<Description>"
/>
```
</CodeBlock>

The resulting HTML will be applied to the Head of the document including all applicable [open graph tags](https://ogp.me/):

```html copy
<meta property="og:image" content="https://res.cloudinary.com/colbycloud-next-cloudinary/image/upload/c_fill,w_1200,h_627,g_center/c_limit,w_1200/f_jpg/q_auto/v1/images/galaxy" />
<meta property="og:image:secure_url" content="https://res.cloudinary.com/colbycloud-next-cloudinary/image/upload/c_fill,w_1200,h_627,g_center/c_limit,w_1200/f_jpg/q_auto/v1/images/galaxy" />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta property="twitter:title" content=" " />
<meta property="twitter:card" content="summary_large_image" />
<meta property="twitter:image" content="https://res.cloudinary.com/colbycloud-next-cloudinary/image/upload/c_fill,w_1200,h_627,g_center/c_limit,w_1200/f_webp/q_auto/v1/images/galaxy" />
```

The `src` property takes in a Cloudinary Public ID which includes the folder path along with the ID of the image itself.
The `twitterTitle` should represent the title of the page and the `alt` value should be a text-based description
of the image.

## Additional Features

You can further take advantage of Cloudinary features like replacing backgrounds with generative AI and text overlays by adding additional props:

<HeaderImage>
<CldImage
src="cld-sample-5"
width="1870"
height="1250"
alt="A variety of colorful and appetizing breakfast dishes, including waffles, oatmeal, and fresh fruits, are arranged on a white surface, accompanied by various condiments and utensils."
sizes="100vw"
crop={{
type: 'fill',
source: true
}}
replaceBackground="cartoon outer space"
overlays={[
{
position: {
y: 40,
x: -10,
gravity: 'south',
},
text: {
color: 'magenta',
fontFamily: 'Source Sans Pro',
fontSize: 160,
fontWeight: 'black',
text: 'OUT OF THIS WORLD'
}
},
{
position: {
y: 50,
gravity: 'south',
},
text: {
color: 'white',
fontFamily: 'Source Sans Pro',
fontSize: 160,
fontWeight: 'black',
text: 'OUT OF THIS WORLD'
}
},
]}
/>
</HeaderImage>

<CodeBlock>
```jsx
---
import { CldOgImage } from 'astro-cloudinary';
---
<CldOgImage
src="<Public ID>"
twitterTitle="<Title>"
alt="<Description>"
crop={{
type: 'fill',
source: true
}}
replaceBackground="cartoon outer space"
overlays={[
{
position: {
y: 40,
x: -10,
gravity: 'south',
},
text: {
color: 'magenta',
fontFamily: 'Source Sans Pro',
fontSize: 160,
fontWeight: 'black',
text: 'OUT OF THIS WORLD'
}
},
{
position: {
y: 50,
gravity: 'south',
},
text: {
color: 'white',
fontFamily: 'Source Sans Pro',
fontSize: 160,
fontWeight: 'black',
text: 'OUT OF THIS WORLD'
}
},
]}
/>
```
</CodeBlock>

[Check out more examples](/cldimage/examples) of what you can do with transformations in the CldImage docs because the CldOgImage supports all the params that the CldImage component does!

## Learn More about CldOgImage with the CldImage docs
* [Configuration](/cldimage/configuration)
* [Examples](/cldimage/examples)
\
66 changes: 66 additions & 0 deletions docs/src/content/docs/cldogimage/configuration.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Configuring CldOgImage
description: Options and configuration for CldOgImage in Astro.
head:
- tag: title
content: CldOgImage Configuration - Astro Cloudinary
---
import CodeBlock from '../../../components/CodeBlock.astro';
import HeaderImage from '../../../components/HeaderImage.astro';
import Table from '../../../components/Table.astro';

The CldOgImage component provides a wide range of options for being able to easily optimize and transform images.

{/* :::tip
Configuration for CldOgImage is the same as [getCldImageUrl](/getcldimageurl/configuration), which both use the same underlying API.
::: */}

## Required Props

The basic props required to use CldOgImage include:

<Table
columns={[
{
id: 'prop',
title: 'Prop'
},
{
id: 'type',
title: 'Type'
},
{
id: 'required',
title: 'Required'
},
{
id: 'example',
title: 'Example'
},
{
id: 'more'
},
]}
data={[
{
prop: 'alt',
type: 'string',
required: 'Yes',
example: () => (<code>Dog catching a frisbee</code>),
},
{
prop: 'twitterTitle',
type: 'string',
required: "Yes",
example: () => (<code>Frisbee Shop</code>),
},
{
prop: 'src',
type: 'string',
required: 'Yes',
example: () => (<code>my-public-id</code>)
}
]}
/>

Beyond that all the extra props that the CldImage component supports can also be applied to the CldOgImage. Take a look at the CldImage [configuration](/cldimage/configuration) for the rest of the properties.
Loading