Rendering images and files
Unlike other headless CMSs, Flashboard does not provide a REST or GraphQL API. You’re responsible for loading the data directly using SQL or a backend like Supabase.
Images inside HTML
Images uploaded inside HTML content will be rendered together with your HTML. Follow our guide on rendering HTML content and that's it.
Images and files uploaded on fields
When you upload images and files on fields, they are stored in your database column using a JSON format that includes everything you need to generate secure URLs for the files and render them. Here's an example:
[
{
"flashboardStorage": "v1",
"serviceName": "s3",
"bucketName": "my-secure-bucket",
"key": "001b8672-76cf-4f6e-9950-798ca09b68bc",
"filename": "my-document.pdf",
"contentType": "application/pdf",
"size": 500316
},
{
"flashboardStorage": "v1",
"serviceName": "s3",
"bucketName": "my-secure-bucket",
"key": "b866d33e-eb03-41c8-b5f9-10496acf9caf",
"filename": "my-image.png",
"contentType": "image/png",
"size": 20165
}
]
If you upload your files on a text
field, it will contain a stringified version of the above format. For json
or jsonb
columns, the actual array of objects
will be stored.
Even if you only upload a single file, it will be stored as an array
with one object. Here's the format of the object representing each uploaded file:
flashboardStorage
(string): the version of the format. Currently, it will always bev1
, and we don't intend to make new versions anytime soon. But we have it there just in case we need to make any breaking changes to the stored format.serviceName
(string): the service used to upload the file. Currently, it will always bes3
, since we only support S3-compatible storage services at the moment. But we plan to add other services in the future.bucketName
(string): the name of the bucket where the file is stored.key
(string): the key of the object in your bucket in UUID format.filename
(string): the original name of the uploaded file.contentType
(string): the Content-Type of the file.size
(number): the size of the file in bytes.
All this information allows you to render your images by connecting to your storage service and generating a signed URL. Follow this guide for in-depth instructions, but here's a simple example in TypeScript using Zod and @aws-sdk/client-s3:
import { GetObjectCommand, S3Client } from '@aws-sdk/client-s3'
import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
import { z } from 'zod'
const fileSchema = z.object({
bucketName: z.string(),
key: z.string(),
contentType: z.string(),
})
const filesSchema = z.array(fileSchema)
function loadMyFilesField() {
// Here you would load the field from the database instead
const myFilesValue = [
{
bucketName: 'my-secure-bucket',
ket: '001b8672-76cf-4f6e-9950-798ca09b68bc',
contentType: 'image/png',
},
{
bucketName: 'my-secure-bucket',
ket: 'b866d33e-eb03-41c8-b5f9-10496acf9caf',
contentType: 'application/pdf',
},
]
return filesSchema.parse(myFilesValue)
}
const myFiles = loadMyFilesField()
const client = new S3Client({
endpoint: 'https://s3.us-east-2.amazonaws.com',
region: 'us-east-2',
credentials: {
accessKeyId: 'my-access-key-id',
secretAccessKey: 'my-secret-access-key',
},
})
myFiles.forEach((file) => {
const { bucketName, key, contentType } = file
const command = new GetObjectCommand({
Bucket: bucketName,
Key: key,
})
// For images, set ResponseContentDisposition to inline for browser rendering
// For other files, set to attachment for immediate download
const disposition = contentType.startsWith('image/') ? 'inline' : 'attachment'
command.input.ResponseContentType = contentType
command.input.ResponseContentDisposition = disposition
// Generate a signed URL that's valid for 15 minutes
const url = getSignedUrl(client, command, { expiresIn: 60 * 15 })
console.log('URL', url)
})

Need help?
Hey! I'm Felipe, Flashboard's founder. I'll personally help you and make sure you get your panel up and running.