Skip to main content

ProductDownloads

Renders a list of downloadable documents associated with a product. Each item is displayed as a styled link that opens in a new tab with a native download prompt. When no documents are available, an empty-state message is shown instead.


Usage

Basic usage on a product detail page

import ProductDownloads from '@/components/propeller/ProductDownloads';

<ProductDownloads
downloads={product.media.documents}
language="EN"
/>

With custom labels

<ProductDownloads
downloads={product.media.documents}
language="NL"
labels={{
title: 'Bestanden',
empty: 'Geen bestanden beschikbaar',
}}
/>

With a custom CSS class

<ProductDownloads
downloads={product.media.documents}
language="EN"
className="mt-6 border-t pt-4"
/>

Conditionally rendering only when documents exist

{product.media?.documents?.items?.length > 0 && (
<ProductDownloads
downloads={product.media.documents}
language="EN"
labels={{ title: 'Product Documents' }}
/>
)}

Inside a tabbed product detail layout

<Tabs>
<Tab label="Description">
<ProductDescription product={product} />
</Tab>
<Tab label="Downloads">
<ProductDownloads
downloads={product.media.documents}
language={currentLanguage}
labels={{ title: 'Technical Documents', empty: 'No documents for this product.' }}
className="p-4"
/>
</Tab>
</Tabs>

Configuration

Data

PropTypeRequiredDescription
downloadsPaginatedMediaDocumentResponseYesThe media documents object from the product. Pass product.media.documents directly.
languagestringYesTwo-letter language code (e.g. "EN", "NL") used to resolve the correct localized document URL and display name.

Customization

PropTypeRequiredDefaultDescription
labelsRecord<string, string>NoSee belowOverride any UI string. Available keys: title, empty.
classNamestringNo""Extra CSS class applied to the root <div> element.

Labels

KeyDefault
title"Downloads"
empty"No downloads"

Behavior

  • Language matching: The component matches the language prop against LocalizedDocument.language and LocalizedString.language. If no match is found, the first entry in each array is used as a fallback. If the array is empty, safe defaults are applied (empty string for URL, "Download" for name).

  • Empty state: When downloads.items is empty, undefined, or has zero length, the title heading is hidden and the empty-state message is displayed.

  • Hidden links: Individual list items where the resolved URL is an empty string are not rendered. This prevents broken download links from appearing.

  • Styling: The component uses Tailwind CSS utility classes. Each download link is rendered as a bordered row with a file icon on the left, a truncated document name in the middle, and a download arrow icon on the right. Hover states change the border and icon colors.

  • No pagination: The component renders all items present in downloads.items at once. If you need pagination, handle it at the query level by adjusting the page and offset arguments on the documents field in your GraphQL query.


GraphQL Query Example

To fetch the data this component needs, include media.documents in your product query:

query ProductWithDownloads($productId: Int!, $language: String) {
product(id: $productId, language: $language) {
productId
name {
value
language
}
media {
documents(page: 1, offset: 100) {
items {
alt {
language
value
}
documents {
language
originalUrl
mimeType
}
}
itemsFound
page
pages
offset
}
}
}
}

Pass the result as:

<ProductDownloads
downloads={product.media.documents}
language="EN"
/>

SDK Services

This component does not call any SDK service directly. It is a pure presentational component that receives pre-fetched data via the downloads prop.

Required product fields

The component reads from the product.media.documents portion of a product query response. The relevant SDK types are:

TypeFields used
PaginatedMediaDocumentResponseitems (array of MediaDocument)
MediaDocumentdocuments (array of LocalizedDocument), alt (array of LocalizedString)
LocalizedDocumentlanguage, originalUrl, mimeType
LocalizedStringlanguage, value

All four types are imported from propeller-sdk-v2.

Field resolution logic

  • Document URL: Finds the first entry in doc.documents matching the provided language. Falls back to the first entry in the array. Returns an empty string if nothing is available (the link is hidden in that case).
  • Document name: Finds the first entry in doc.alt matching the provided language. Falls back to the first entry, then to the string "Download".
  • MIME type: Resolved the same way as the URL (language match, then first entry). Currently resolved internally but not displayed in the UI.