Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions docs/content/3.usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,8 @@ const version = useStrapiVersion()

This composable is a helper to get the full URL for media. Strapi endpoints return media URLs as relative paths (e.g. `/uploads/image.png`) on self-hosted instances, or absolute URLs on Strapi Cloud. This composable handles both cases automatically.

Relative media paths use `runtimeConfig.public.strapi.url`, including during server-side rendering and static generation, so the URLs are accessible to browsers even when the server uses a different internal Strapi URL.

```vue
<script setup>
const imageUrl = useStrapiMedia('/uploads/image.png')
Expand Down
2 changes: 1 addition & 1 deletion src/runtime/composables/useStrapiMedia.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const useStrapiMedia = (path: string): string => {
return path
}

const config = import.meta.server ? useRuntimeConfig() : useRuntimeConfig().public
const config = useRuntimeConfig().public

return joinURL(config.strapi.url, path)
}
30 changes: 30 additions & 0 deletions test/e2e/media.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { readFile } from 'node:fs/promises'
import { join } from 'node:path'
import { describe, expect, it } from 'vitest'
import { $fetch, setup, useTestContext } from '@nuxt/test-utils/e2e'

const expectPublicMedia = (html: string) => {
expect(html).toContain('src="https://media.example.com/uploads/image.jpg"')
expect(html).toContain('src="https://media.example.com/uploads/no-leading-slash.jpg"')
expect(html).toContain('src="https://cdn.example.com/image.jpg"')
expect(html).toContain('src="http://cdn.example.com/image.jpg"')
expect(html).not.toContain('src="http://localhost:1337/')
expect(html).toContain('id="api-url">http://localhost:1337/api</p>')
}

describe('Strapi media with separate client/server URLs', async () => {
await setup({
rootDir: new URL('../fixtures/media', import.meta.url).pathname
})

it('uses the public media URL during server rendering', async () => {
expectPublicMedia(await $fetch('/'))
})

it('uses the public media URL in prerendered HTML', async () => {
const { nuxt } = useTestContext()
const html = await readFile(join(nuxt.options.nitro.output.dir, 'public/prerender/index.html'), 'utf8')

expectPublicMedia(html)
})
})
25 changes: 25 additions & 0 deletions test/fixtures/media/app.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script setup lang="ts">
import { useStrapiMedia, useStrapiUrl } from '#imports'

const mediaUrls = [
'/uploads/image.jpg',
'uploads/no-leading-slash.jpg',
'https://cdn.example.com/image.jpg',
'http://cdn.example.com/image.jpg'
].map(path => useStrapiMedia(path))
const apiUrl = useStrapiUrl()
</script>

<template>
<div>
<img
v-for="url in mediaUrls"
:key="url"
:src="url"
alt="Strapi media"
>
<p id="api-url">
{{ apiUrl }}
</p>
</div>
</template>
18 changes: 18 additions & 0 deletions test/fixtures/media/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { defineNuxtConfig } from 'nuxt/config'
import strapi from '../../../src/module'

export default defineNuxtConfig({
modules: [strapi],
pages: false,
devtools: { enabled: false },
runtimeConfig: {
strapi: { url: 'http://localhost:1337', token: '' },
public: {
strapi: { url: 'https://media.example.com', token: '' }
}
},
compatibilityDate: '2026-05-06',
nitro: {
prerender: { routes: ['/prerender'], crawlLinks: false }
}
})
13 changes: 8 additions & 5 deletions test/unit/useStrapiMedia.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ import { describe, expect, it, vi } from 'vitest'
import { useStrapiMedia } from '../../src/runtime/composables/useStrapiMedia'

const mockConfig = {
strapi: { url: 'http://localhost:1337', prefix: '/api', version: 'v5' }
strapi: { url: 'http://localhost:1337', prefix: '/api', version: 'v5' },
public: {
strapi: { url: 'https://media.example.com', prefix: '/api', version: 'v5' }
}
}

vi.mock('#imports', () => ({
useRuntimeConfig: () => ({ public: mockConfig })
useRuntimeConfig: () => mockConfig
}))

describe('useStrapiMedia', () => {
Expand All @@ -26,13 +29,13 @@ describe('useStrapiMedia', () => {
.toBe('https://domain.media.strapiapp.com/uploads/image.jpg')
})

it('joins relative paths with strapi URL', () => {
it('joins relative paths with the public Strapi URL', () => {
expect(useStrapiMedia('/uploads/image.jpg'))
.toBe('http://localhost:1337/uploads/image.jpg')
.toBe('https://media.example.com/uploads/image.jpg')
})

it('joins paths without leading slash', () => {
expect(useStrapiMedia('uploads/image.jpg'))
.toBe('http://localhost:1337/uploads/image.jpg')
.toBe('https://media.example.com/uploads/image.jpg')
})
})
Loading