Skip to content

Client configuration and plugins

Customize the Silex editor with plugins, publication transformers, and settings sections.

Overview

The client config file runs in the browser when the editor loads. It lets you add plugins (from GrapesJS or custom), define how websites are published, and extend the settings UI. Like the server config, it's a plugin that receives a ClientConfig object.

Prerequisites

  • Server configured with SILEX_CLIENT_CONFIG environment variable pointing to your config file
  • Understanding of publication transformers
  • Basic JavaScript knowledge

Setup

1. Create a client config file

Create client-config.js in your Silex instance directory:

// client-config.js
export default async function (config) {
  console.log('Client config loading')
  // Add plugins, transformers, settings here
}

2. Configure the server to serve it

Set the environment variable:

SILEX_CLIENT_CONFIG=./client-config.js npm start

Or in Docker:

environment:
  SILEX_CLIENT_CONFIG: /silex/client-config.js

Mount the file into the container:

volumes:
  - ./client-config.js:/silex/client-config.js:ro

3. The file is loaded at startup

When users open the editor, Silex fetches your client config from http://your-silex.com/client-config.js?version and executes it. Changes are picked up on page reload.

ClientConfig API

class ClientConfig extends Config {
  // Properties
  websiteId: WebsiteId
  storageId: ConnectorId
  lang: string
  rootUrl: string
  debug: boolean
  addHashInCssFileName: boolean
  replacedElements: string[]
  grapesJsConfig: EditorConfig
  clientConfigUrl: string
  fontsApiKey: string
  fontsServerUrl: string
  fontsApiUrl: string
  cmsConfig: Partial<EleventyPluginOptions> & { enabled?: boolean }
  publicationTransformers: Array<PublicationTransformer>

  // Methods
  getEditor(): Editor
  initGrapesConfig(): void
  addPlugin(plugin: Plugin | Plugin[], options?: any): Promise<void>
  addPublicationTransformers(
    transformers: PublicationTransformer | PublicationTransformer[]
  ): void
  resetPublicationTransformers(): void
  addDefaultPlugins(): Promise<void>
  addSettings(                           // deprecated
    section: SettingsSection,
    siteOrPage: 'site' | 'page',
    position?: 'first' | 'last' | number
  ): void
  removeSettings(id: string, siteOrPage: 'site' | 'page'): void
}

Adding plugins

GrapesJS plugins

Load any GrapesJS plugin via npm:

// client-config.js
import grapesJsCustomCode from 'grapesjs-custom-code'
import grapesJsTooltip from 'grapesjs-tooltip'

export default async function (config) {
  // Add plugins to the GrapesJS editor
  config.grapesJsConfig.plugins = [
    ...config.grapesJsConfig.plugins,
    grapesJsCustomCode,
    grapesJsTooltip,
  ]

  // Plugin options (optional)
  config.grapesJsConfig.pluginsOpts = {
    ...config.grapesJsConfig.pluginsOpts,
    [grapesJsCustomCode]: { /* options */ },
    [grapesJsTooltip]: { /* options */ },
  }
}

Custom plugins

Create your own plugin module:

// plugins/my-plugin.js
export default {
  name: 'MyPlugin',
  async init(editor, options) {
    console.log('My plugin initialized', options)

    // Add a custom block
    editor.Blocks.add('my-block', {
      label: 'My Block',
      content: '<div>Custom block</div>',
      category: 'Basic',
      media: '<svg>...</svg>',
    })

    // Listen to editor events
    editor.on('canvas:render', () => {
      console.log('Canvas rendered')
    })
  },
}

Then load it in your config:

// client-config.js
import myPlugin from './plugins/my-plugin.js'

export default async function (config) {
  config.grapesJsConfig.plugins = [
    ...config.grapesJsConfig.plugins,
    myPlugin,
  ]

  config.grapesJsConfig.pluginsOpts = {
    ...config.grapesJsConfig.pluginsOpts,
    MyPlugin: {
      someOption: 'value',
    },
  }
}

Adding publication transformers

Publication transformers control how websites are rendered and published. See Publication transformers for details.

Example: Transform file paths for a CDN:

export default async function (config) {
  config.addPublicationTransformers({
    transformPath(path, type) {
      // Rewrite CSS paths to a CDN
      if (type === 'css') {
        return path.replace(/\.css$/, '.min.css')
      }
      return path
    },

    transformPermalink(link, type, initiator) {
      // Point all assets to a CDN
      if (!link.startsWith('http')) {
        return 'https://cdn.example.com/' + link
      }
      return link
    },

    transformFile(file) {
      // Minify HTML (example using a hypothetical lib)
      if (file.type === 'html') {
        file.content = minifyHtml(file.content)
      }
      return file
    },
  })
}

Multiple transformers

Chain multiple transformers:

export default async function (config) {
  // Transformer 1: Rewrite paths
  config.addPublicationTransformers({
    transformPath(path, type) {
      return path.startsWith('/') ? path : '/' + path
    },
  })

  // Transformer 2: Add cache busting
  config.addPublicationTransformers({
    transformFile(file) {
      const now = Date.now()
      file.name = file.name.replace(/\.(\w+)$/, `.${now}.$1`)
      return file
    },
  })
}

Transformers run in order, each one receiving the output of the previous.

Listening to client events

Access the editor and listen to its events:

const { ClientEvent } = require('@silexlabs/silex/dist/client/events')

export default async function (config) {
  // Wait for GrapesJS to load
  config.on(ClientEvent.GRAPESJS_END, (editor) => {
    console.log('Editor is ready', editor)

    // Listen to publication events
    editor.on(ClientEvent.PUBLISH_START, (data) => {
      console.log('Publishing website:', data.siteSettings.name)
    })

    editor.on(ClientEvent.PUBLISH_END, (result) => {
      console.log('Publication result:', result)
    })

    editor.on(ClientEvent.SETTINGS_SAVE_START, (data) => {
      console.log('Saving settings')
    })
  })
}

Client events

Event Fired on Purpose
STARTUP_START config Silex is starting (page loading)
STARTUP_END config Silex is ready
GRAPESJS_START config GrapesJS is about to initialize
GRAPESJS_END editor GrapesJS is ready
PUBLICATION_UI_OPEN editor Publish dialog opened
PUBLICATION_UI_CLOSE editor Publish dialog closed
PUBLISH_BEFORE editor Before publication starts
PUBLISH_START editor Publication starts
PUBLISH_PAGE editor Publishing a single page
PUBLISH_DATA editor Before sending data to server
PUBLISH_END editor Publication complete
PUBLISH_ERROR editor Publication failed
PUBLISH_LOGIN_START editor Before connector login
PUBLISH_LOGIN_END editor After connector login
SETTINGS_OPEN editor Settings dialog opened
SETTINGS_CLOSE editor Settings dialog closed
SETTINGS_SAVE_START editor Settings about to save
SETTINGS_SAVE_END editor Settings saved

Configuring GrapesJS

Modify editor behavior before it loads:

export default async function (config) {
  // Customize GrapesJS config
  config.grapesJsConfig = {
    ...config.grapesJsConfig,
    canvas: {
      styles: ['https://fonts.googleapis.com/css?family=Roboto'],
      scripts: [],
    },
    allowScripts: 0, // Disable script tags in canvas
    blockManager: {
      blocks: [], // Clear default blocks
    },
  }
}

Configuring CMS

The CMS is built into Silex and enabled by default. Customize it:

export default async function (config) {
  config.cmsConfig = {
    enabled: true,
    enable11ty: true,
    cacheBuster: false,
    dataSources: [],
    dir: {
      input: '',
      html: '',
      assets: 'assets',
      css: 'css',
    },
    urls: {
      css: '/css',
      assets: '/assets',
    },
  }
}

For CMS configuration details, see Build awesome configuration.

Complete example

// client-config.js
import grapesJsCustomCode from 'grapesjs-custom-code'
import myPlugin from './plugins/my-plugin.js'
import { ClientEvent } = require('@silexlabs/silex/dist/client/events')

export default async function (config) {
  // Add plugins
  config.grapesJsConfig.plugins = [
    ...config.grapesJsConfig.plugins,
    grapesJsCustomCode,
    myPlugin,
  ]

  config.grapesJsConfig.pluginsOpts = {
    ...config.grapesJsConfig.pluginsOpts,
    [grapesJsCustomCode]: { languages: ['javascript', 'css'] },
    MyPlugin: { apiKey: process.env.MY_API_KEY },
  }

  // Add publication transformers
  config.addPublicationTransformers({
    transformPath(path, type) {
      return path.replace(/\.html$/, '')
    },
    transformPermalink(link) {
      if (!link.startsWith('http') && !link.startsWith('/')) {
        return '/' + link
      }
      return link
    },
  })

  // Listen to events
  config.on(ClientEvent.GRAPESJS_END, (editor) => {
    editor.on(ClientEvent.PUBLISH_END, (result) => {
      if (result.success) {
        console.log(`Published to ${result.url}`)
      } else {
        console.error(`Publication failed: ${result.message}`)
      }
    })
  })

  // Customize CMS
  config.cmsConfig.dataSources = [
    {
      id: 'wordpress',
      type: 'graphql',
      url: 'https://my-wordpress.com/graphql',
    },
  ]
}

Troubleshooting

Config file not loading

Check the browser console for fetch errors:

  1. Open DevTools (F12)
  2. Check Console tab for 404 or syntax errors
  3. Check Network tab: client-config.js?version should return 200

Ensure SILEX_CLIENT_CONFIG is set:

env | grep SILEX_CLIENT_CONFIG

Plugins not loaded

Add debug logging:

export default async function (config) {
  console.log('Client config is running')
  console.log('GrapesJS config:', config.grapesJsConfig)
  // ... add plugins
}

Check the browser console for errors during plugin initialization.

Publication transformers not applied

Verify they're added before the default transformer:

export default async function (config) {
  // Add custom transformer FIRST
  config.resetPublicationTransformers()
  config.addPublicationTransformers(myTransformer)
}

See also

Edit this page on GitLab