diff --git a/public/logos/cloudinary.svg b/public/logos/cloudinary.svg
new file mode 100644
index 0000000000000..1b27a350c6848
--- /dev/null
+++ b/public/logos/cloudinary.svg
@@ -0,0 +1,11 @@
+
\ No newline at end of file
diff --git a/src/components/MediaGuidesNav.astro b/src/components/MediaGuidesNav.astro
new file mode 100644
index 0000000000000..2ba928eb09108
--- /dev/null
+++ b/src/components/MediaGuidesNav.astro
@@ -0,0 +1,26 @@
+---
+import { englishPages } from '~/content';
+import { isMediaEntry } from '~/content/config';
+import { isLogoKey } from '~/data/logos';
+import { getLanguageFromURL } from '~/util';
+import CardsNav from './NavGrid/CardsNav.astro';
+
+const lang = getLanguageFromURL(Astro.url.pathname);
+const enPages = englishPages.filter(isMediaEntry);
+
+const links = enPages
+ .sort((a, b) => {
+ // Sort alphabetically.
+ return a.data.service.toLowerCase() > b.data.service.toLowerCase() ? 1 : -1;
+ })
+ .map((page) => {
+ const { service } = page.data;
+ const pageUrl = '/' + page.slug.replace('en/', `${lang}/`) + '/';
+ const logo = isLogoKey(page.slug.split('/').pop());
+ return { title: service, href: pageUrl, logo };
+ });
+---
+
+
+
+
diff --git a/src/components/starlight/MarkdownContent.astro b/src/components/starlight/MarkdownContent.astro
index d1d1d0dcf7743..bd16e19cf957e 100644
--- a/src/components/starlight/MarkdownContent.astro
+++ b/src/components/starlight/MarkdownContent.astro
@@ -4,6 +4,7 @@ import '@astrojs/starlight/style/markdown.css';
import BackendGuidesNav from '../BackendGuidesNav.astro';
import CMSGuidesNav from '../CMSGuidesNav.astro';
import DeployGuidesNav from '../DeployGuidesNav.astro';
+import MediaGuidesNav from '../MediaGuidesNav.astro';
import IntegrationsNav from '../IntegrationsNav.astro';
import MigrationGuidesNav from '../MigrationGuidesNav.astro';
import { getPageCategory } from '~/util/getPageCategory';
@@ -41,6 +42,14 @@ const { entry } = Astro.props;
>
)
}
+ {
+ entry.data.type === 'media' && (
+ <>
+
{t('media.navTitle')}
+
+ >
+ )
+ }
{
entry.data.type === 'integration' && (
<>
diff --git a/src/content/config.ts b/src/content/config.ts
index bcc42fae147d2..4fbc500fed9f4 100644
--- a/src/content/config.ts
+++ b/src/content/config.ts
@@ -46,6 +46,12 @@ export const integrationSchema = baseSchema.extend({
githubIntegrationURL: z.string().url(),
});
+export const mediaSchema = baseSchema.extend({
+ type: z.literal('media'),
+ stub: z.boolean().default(false),
+ service: z.string(),
+});
+
export const migrationSchema = baseSchema.extend({
type: z.literal('migration'),
framework: z.string(),
@@ -68,6 +74,7 @@ export const docsCollectionSchema = z.union([
backendSchema,
cmsSchema,
integrationSchema,
+ mediaSchema,
migrationSchema,
tutorialSchema,
deploySchema,
@@ -110,6 +117,8 @@ export const isIntegrationEntry = createIsDocsEntry('integration');
export const isTutorialEntry = createIsDocsEntry('tutorial');
+export const isMediaEntry = createIsDocsEntry('media');
+
export const isMigrationEntry = createIsDocsEntry('migration');
export const isRecipeEntry = createIsDocsEntry('recipe');
diff --git a/src/content/docs/en/guides/media.mdx b/src/content/docs/en/guides/media.mdx
new file mode 100644
index 0000000000000..bba99a88763f4
--- /dev/null
+++ b/src/content/docs/en/guides/media.mdx
@@ -0,0 +1,39 @@
+---
+title: Use a DAM with Astro
+description: How to use a Digital Asset Manager (DAM) to add images and videos to Astro
+i18nReady: true
+---
+import MediaGuidesNav from '~/components/MediaGuidesNav.astro';
+import ReadMore from '~/components/ReadMore.astro';
+import Badge from "~/components/Badge.astro"
+
+**Ready to connect a headless Digital Asset Manager (DAM) to your Astro project?** Follow one of our guides to integrate a hosted media system.
+
+:::tip
+Find [community-maintained integrations](https://astro.build/integrations/) for connecting a DAM or hosted media system to your project in our integrations directory.
+:::
+
+## Hosted Media Guides
+
+Note that many of these pages are **stubs**: they're collections of resources waiting for your contribution!
+
+
+
+## Why use a DAM or hosted media?
+
+Using a DAM, or Digital Asset Manager, helps individuals, teams, and organizations manage their image and video assets from a central location much like a [CMS](/en/guides/cms/).
+
+The difference is the type of content being managed: a DAM would primarily manage images, videos, other media assets like 3D models, and any metadata associated with those assets.
+
+This can be useful particularly when using a single source of truth for your assets between multiple web or mobile properties. This is important if you're part of an organization that requires multiple teams to use the same assets, or are integrating into other content systems like a PIM (Product Information Manager) to connect your assets to products.
+
+## Which hosted media systems or DAMs work well with Astro?
+
+Much like a CMS, because Astro manages the _presentation_ of your content, you'll want to use a headless DAM, that allows you to fetch and interact with your assets via an API or SDK.
+
+Some headless DAMs, like Cloudinary, provide an Astro [integration](/en/guides/integrations-guide/) that allows you to easily fetch your assets as well as display them on your website or app.
+
+## Can I use Astro without a hosted media system or DAM?
+
+Yes! Astro provides built-in ways to [store images](/en/guides/images/#where-to-store-images), including support for referencing remote images.
+
diff --git a/src/content/docs/en/guides/media/cloudinary.mdx b/src/content/docs/en/guides/media/cloudinary.mdx
new file mode 100644
index 0000000000000..72ccfe55526d3
--- /dev/null
+++ b/src/content/docs/en/guides/media/cloudinary.mdx
@@ -0,0 +1,16 @@
+---
+title: Cloudinary & Astro
+description: Add images and videos to your Astro project using Cloudinary
+type: media
+stub: true
+service: Cloudinary
+i18nReady: true
+---
+
+[Cloudinary](https://cloudinary.com) is an image and video platform and headless Digital Asset Manager (DAM) that lets you host assets and deliver them from their content delivery network (CDN).
+
+When delivering from Cloudinary, you additionally get access to their transformation engine, giving you the ability to edit your assets with tools like background removal, dynamic cropping and resizing, and generative AI.
+
+## Official Resources
+
+- Check out [the Cloudinary Astro SDK](https://astro.cloudinary.dev/) (in Beta)
diff --git a/src/data/logos.ts b/src/data/logos.ts
index f947a092a5cac..3a7772a09cd3a 100644
--- a/src/data/logos.ts
+++ b/src/data/logos.ts
@@ -9,6 +9,7 @@ export const logos = LogoCheck({
buddy: { file: 'buddy.svg', padding: '.1625em' },
cleavr: { file: 'cleavr.svg', padding: '0.125em 0.125em 0.1375em' },
cloudflare: { file: 'cloudflare-pages.svg', padding: '.1875em' },
+ cloudinary: { file: 'cloudinary.svg', padding: '.1875em' },
'craft-cms': { file: 'craft-cms.svg', padding: '.225em' },
crystallize: { file: 'crystallize.svg', padding: '.1875em' },
'create-react-app': { file: 'create-react-app.svg', padding: '.1875em' },
diff --git a/src/i18n/en/nav.ts b/src/i18n/en/nav.ts
index 0bab2901f520f..a9a330d69dd3b 100644
--- a/src/i18n/en/nav.ts
+++ b/src/i18n/en/nav.ts
@@ -66,6 +66,7 @@ export default [
{ text: 'CSS & Styling', slug: 'guides/styling', key: 'guides/styling' },
{ text: 'Images', slug: 'guides/images', key: 'guides/images' },
{ text: 'Fonts', slug: 'guides/fonts', key: 'guides/fonts' },
+ { text: 'Connect Hosted Media or DAM', slug: 'guides/media', key: 'guides/media' },
{ text: 'Connect Your Data', header: true, type: 'learn', key: 'data' },
{ text: 'Data Fetching', slug: 'guides/data-fetching', key: 'guides/data-fetching' },
diff --git a/src/i18n/en/ui.ts b/src/i18n/en/ui.ts
index 1fc5e0417fbd6..b21214faa181b 100644
--- a/src/i18n/en/ui.ts
+++ b/src/i18n/en/ui.ts
@@ -50,6 +50,8 @@ export default {
'deploy.staticTag': 'Static',
// CMS Guides vocabulary
'cms.navTitle': 'More CMS guides',
+ // Media Guides voccabulary
+ 'media.navTitle': 'More DAM guides',
// Migration Guides vocabulary
'migration.navTitle': 'More migration guides',
// Recipes vocabulary
diff --git a/src/util/getPageCategory.ts b/src/util/getPageCategory.ts
index efee7049b09f1..f4d8513c91c8e 100644
--- a/src/util/getPageCategory.ts
+++ b/src/util/getPageCategory.ts
@@ -9,6 +9,7 @@ const categories = [
['guides/backend/', 'Recipes'],
['guides/cms/', 'Recipes'],
['guides/deploy/', 'Recipes'],
+ ['guides/media/', 'Recipes'],
['guides/integrations-guide/', 'Learn'],
['guides/migrate-to-astro/', 'Recipes'],
['guides/upgrade-to/', 'Upgrade Guides'],
diff --git a/src/util/isSubPage.ts b/src/util/isSubPage.ts
index 3d04597023965..0a4d979f93ef3 100644
--- a/src/util/isSubPage.ts
+++ b/src/util/isSubPage.ts
@@ -6,7 +6,7 @@ import { getPageCategory } from './getPageCategory';
export function removeSubPageSegment(path: string) {
// Include new pages with sub-pages as part of this regex.
const regex =
- /(?:install|deploy|integrations-guide|tutorial|migrate-to-astro|recipes|cms|backend)\//;
+ /(?:install|deploy|integrations-guide|tutorial|migrate-to-astro|recipes|cms|media|backend)\//;
const matches = regex.exec(path);
if (matches) {