Skip to content

Custom Slide Types

Build custom slide types for specialized content.

Deckyard’s slide type system is extensible, allowing you to create custom slide types for your specific needs. Custom slide types live in the /custom-slide-types/ directory and are automatically loaded at startup.

Slide Types settings showing Custom Slide Types section with Create Type button, and Slide Type Curation with toggles for each built-in type

deckyard/
├── custom-slide-types/
│ ├── my-custom-slide.js
│ ├── team-profile.js
│ └── product-showcase.js
└── ...
  • Use kebab-case: my-slide-type.js
  • Filename becomes the type name: my-slide-type
  • Files starting with _ are skipped (helpers)
  • Only .js files are loaded
export default {
label: 'My Custom Slide',
fields: [],
renderHtml(content) {
return '<div class="slide">Hello World</div>';
}
};
PropertyTypeDescription
labelstringDisplay name in slide picker
renderHtmlfunctionReturns HTML string
PropertyTypeDescription
fieldsarrayEditable field definitions
defaultsobjectDefault values for fields
iconstringIcon name for slide picker
categorystringCategory grouping
fields: [
{
key: 'title',
label: 'Title',
type: 'string',
default: 'Untitled'
},
{
key: 'description',
label: 'Description',
type: 'markdown'
},
{
key: 'image',
label: 'Background Image',
type: 'image'
}
]
TypeEditor ControlExample Value
stringText input"Hello"
markdownRich text editor"**Bold** text"
imageImage picker"/uploads/img.jpg"
imagesImage gallery["/img1.jpg", "/img2.jpg"]
booleanToggle switchtrue
selectDropdown"option1"
numberNumber input42
colorColor picker"#ff0000"
{
key: 'layout',
label: 'Layout',
type: 'select',
options: [
{ value: 'left', label: 'Left aligned' },
{ value: 'center', label: 'Centered' },
{ value: 'right', label: 'Right aligned' }
],
default: 'center'
}
renderHtml(content, slide, ctx) {
// content: Field values from user
// slide: Full slide object (id, type, content)
// ctx: Rendering context (theme, locale, etc.)
return '<div>...</div>';
}
ParameterDescription
contentObject with field values
slideComplete slide object
ctxContext: theme, followCodes, locale

Use the helper to prevent XSS:

import { esc } from '../shared/slide-types/helpers.js';
renderHtml(content) {
return `<h1>${esc(content.title)}</h1>`;
}
import { esc } from '../shared/slide-types/helpers.js';
export default {
label: 'Team Profile',
fields: [
{ key: 'name', label: 'Name', type: 'string', default: '' },
{ key: 'role', label: 'Role', type: 'string', default: '' },
{ key: 'photo', label: 'Photo', type: 'image' },
{ key: 'bio', label: 'Biography', type: 'markdown' }
],
renderHtml(content) {
const photoHtml = content.photo
? `<img src="${esc(content.photo)}" alt="${esc(content.name)}" class="profile-photo" />`
: '';
return `
<div class="slide slide-team-profile">
<div class="profile-card">
${photoHtml}
<h1>${esc(content.name)}</h1>
<p class="role">${esc(content.role)}</p>
<div class="bio">${content.bio}</div>
</div>
</div>
`;
}
};

Leverage built-in slide classes:

renderHtml(content) {
return `
<div class="slide slide-center">
<div class="slide-inner">
<h1 class="slide-title">${esc(content.title)}</h1>
</div>
</div>
`;
}

Add styles via a companion CSS file or inline:

renderHtml(content) {
return `
<style>
.my-custom-slide .feature-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 2rem;
}
</style>
<div class="slide my-custom-slide">
<div class="feature-grid">...</div>
</div>
`;
}

Access theme variables in your styles:

.my-custom-slide {
background: var(--t-background);
color: var(--t-text);
}
const COPY = {
nl: { title: 'Welkom' },
'en-GB': { title: 'Welcome' }
};
export default {
label: 'Welcome Slide',
fields: [],
renderHtml(content, slide, ctx) {
const lang = ctx?.locale || 'en-GB';
const copy = COPY[lang] || COPY['en-GB'];
return `<h1>${esc(copy.title)}</h1>`;
}
};

String and markdown fields are automatically included in Deckyard’s translation workflow.

export default {
label: 'Feature Slide',
fields: [
{ key: 'title', type: 'string' },
{ key: 'layout', type: 'select', options: [...] }
],
defaults: {
title: 'New Feature',
layout: 'grid'
}
};

On server startup:

  1. /custom-slide-types/ scanned for .js files
  2. Each file dynamically imported
  3. Default export validated
  4. Type registered with slide system

Check console for load status:

[custom-loader] Loaded custom slide type: team-profile
[custom-loader] Loaded custom slide type: product-showcase

If a type fails to load:

[custom-loader] Skipping broken-slide.js: missing 'label' property
[custom-loader] Error loading bad-code.js: SyntaxError: ...
  • Focus on single-purpose slides
  • Reuse existing styling where possible
  • Don’t over-engineer
  • Use semantic HTML
  • Include alt text for images
  • Ensure color contrast
  • Support keyboard navigation
  • Avoid heavy JavaScript in renders
  • Optimize images
  • Keep HTML clean and minimal
  • Document your slide types
  • Use consistent naming
  • Test with different content lengths

For forks:

  • /custom-slide-types/ tracked in fork
  • Ignored in upstream OSS repo
  • Merge conflicts rare

When updating from upstream:

  • Custom slides are preserved
  • Core slide types may change
  • Test custom slides after updates
  • Check file is in correct directory
  • Verify file extension is .js
  • Check for syntax errors
  • Ensure default export exists
  • Verify field definitions
  • Check key is unique
  • Ensure type is valid
  • Check browser console for errors
  • Verify HTML is valid
  • Test with minimal content first