Skip to main content

Shesha Developer Guide

A comprehensive guide to developing configurable components in the Shesha low-code platform.

Prerequisites

  • Node.js v14+
  • npm or yarn
  • Git

Getting Started

# Fork and clone the repository
git fork https://github.com/your-organization/shesha-framework.git
cd shesha-framework

# Install dependencies
cd shesha-reactjs
npm install

# Start development server
npm run dev

Architecture Overview

Shesha is a low-code platform built on:

  • NextJS + TypeScript
  • Ant Design components

Project Structure

shesha-framework/
├── shesha-reactjs/ # Frontend React project
│ ├── src/
│ │ ├── components/ # Reusable components
│ │ ├── designer-components/ # Form designer components
│ │ │ ├── _common/ # Shared utilities
│ │ │ ├── _settings/ # Settings components
│ │ │ └── textField/ # Example component
│ │ ├── hooks/ # Custom React hooks
│ │ ├── providers/ # Context providers
│ │ └── styles/ # Styling system
└── shesha-backend/ # Backend project

Component Development

Core Files Structure

Every Shesha component requires 3 essential files:

designer-components/myComponent/
├── index.tsx # Main component implementation
├── interfaces.ts # TypeScript interfaces
├── settingsForm.ts # Configuration settings
├── styles.ts # Component styling (optional)
└── utils.ts # Helper functions (optional)

1. Component Interface (interfaces.ts)

import { IConfigurableFormComponent } from '@/providers/form/models';

export interface IMyComponentProps extends IConfigurableFormComponent {
placeholder?: string;
customValue?: string;
validate?: {
required?: boolean;
minLength?: number;
message?: string;
};
}

2. Settings Configuration (settingsForm.ts)

import { DesignerToolbarSettings } from '@/interfaces/toolbarSettings';
import { nanoid } from '@/utils/uuid';

export const getSettings = (data: IMyComponentProps) => {
const commonTabId = nanoid();

return {
components: new DesignerToolbarSettings(data)
.addSearchableTabs({
id: nanoid(),
propertyName: 'settingsTabs',
parentId: 'root',
label: 'Settings',
hideLabel: true,
tabs: [{
key: '1',
title: 'Common',
id: commonTabId,
components: [
...new DesignerToolbarSettings()
.addSettingsInput({
id: nanoid(),
propertyName: 'placeholder',
label: 'Placeholder',
inputType: 'textField',
jsSetting: true,
})
.toJson()
]
}]
})
.toJson(),
formSettings: {
layout: 'vertical',
labelCol: { span: 24 },
wrapperCol: { span: 24 }
}
};
};

3. Component Implementation (index.tsx)

import React from 'react';
import { IToolboxComponent } from '@/interfaces';
import { Input } from 'antd';
import { ComponentOutlined } from '@ant-design/icons';
import { IMyComponentProps } from './interfaces';
import { getSettings } from './settingsForm';

const MyComponent: IToolboxComponent<IMyComponentProps> = {
type: 'myComponent',
name: 'My Component',
icon: <ComponentOutlined />,
isInput: true,

Factory: ({ model }) => {
if (model.hidden) return null;

return (
<Input
placeholder={model.placeholder}
disabled={model.disabled}
/>
);
},

settingsFormMarkup: (data) => getSettings(data),

initModel: (model) => ({
...model,
label: 'My Component',
placeholder: 'Enter text...'
}),

migrator: (m) => m,
};

export default MyComponent;
note

Your component model contains properties primarily from the component's properties panel.

4. Component Registration

Add your component to
src/providers/form/defaults/toolboxComponents.ts:

import MyComponent from '@/designer-components/myComponent';

export const getToolboxComponents = (): IToolboxComponentGroup[] => [
{
name: 'Data Entry',
visible: true,
components: [
TextField,
NumberField,
MyComponent, // Add your component here
],
},
// ... other groups
];

The visible property determines whether your component will appear in the widgets panel.

Settings System

Common Setting Types

Basic Input

.addSettingsInput({
id: nanoid(),
propertyName: 'label',
label: 'Label',
inputType: 'textField',
jsSetting: true,
})

Input Row (Multiple Fields)

.addSettingsInputRow({
id: nanoid(),
hidden: {
_code: 'return getSettingValue(data?.background?.type) !== "color";',
_mode: 'code',
_value: false
} as any,
parentId: appearanceTabId,
inputs: [{
type: 'colorPicker',
id: 'backgroundStyleRow-color',
label: "Color",
propertyName: "background.color",
hideLabel: true,
jsSetting: false,
}],
})

Collapsible Panel

.addCollapsiblePanel({
id: nanoid(),
propertyName: 'styling',
label: 'Styling Options',
content: {
id: nanoid(),
components: [
// Panel contents...
],
},
})

Conditional Visibility

Show/Hide Based on Boolean

hidden: { 
_code: 'return getSettingValue(data?.background?.type) !== "color";',
_mode: 'code',
_value: false
} as any

Show Based on Selected Value

hidden: { 
_code: 'return getSettingValue(data?.background?.type) === "color";',
_mode: 'code',
_value: false
} as any

Responsive Design with Property Router

.addPropertyRouter({
id: nanoid(),
propertyName: 'deviceSettings',
label: 'Device Settings',
propertyRouteName: {
_mode: 'code',
_code: "return contexts.canvasContext?.designerDevice || 'desktop';",
},
components: [
// Device-specific settings...
],
})

Styling System

Creating Styles (styles.ts)

import { createStyles } from '@/styles';

export const useStyles = createStyles(({ css, cx }, props) => {
const myComponent = cx("sha-my-component", css`
.ant-input {
font-weight: ${props.fontWeight};
color: ${props.color};
border-radius: ${props.borderRadius}px;
}
`);

return { myComponent };
});

Using Styles in Components

import { useStyles } from './styles';

const MyComponent = (props) => {
const { classes } = useStyles({
fontWeight: props.font?.weight,
color: props.font?.color,
borderRadius: props.borderRadius,
});

return (
<div className={classes.myComponent}>
{/* Component content */}
</div>
);
};

Shesha Hooks

useForm – Form Context

import { useForm } from '@/providers/form';

const { form, formMode, formData } = useForm();

// Read values
const value = form.getValues('fieldName');

// Set values
form.setFieldValue('fieldName', 'newValue');

// Submit form
form.submit();

useSheshaApplication – App Services

import { useSheshaApplication } from '@/providers/sheshaApplication';

const { httpClient, backendUrl } = useSheshaApplication();

// Make API calls
const response = await httpClient.get(`${backendUrl}/api/data`);

useConfigurableAction – Execute Actions

import { useConfigurableAction } from '@/providers/configurableActionsDispatcher';

const { execute, loading } = useConfigurableAction({
actionName: 'submit',
actionOwner: 'form'
});

execute({
data: { /* action data */ },
onSuccess: () => console.log('Success!'),
onError: (error) => console.error(error)
});

Ant Design Best Practices

Form Components

import { ConfigurableFormItem } from '@/components/configurableForm/configurableFormItem';

<ConfigurableFormItem model={model}>
<Input placeholder={model.placeholder} />
</ConfigurableFormItem>

Layout & Spacing

// Grid System
<Row gutter={[16, 16]}>
<Col span={12}>Left</Col>
<Col span={12}>Right</Col>
</Row>

// Component Spacing
<Space>
<Button>Cancel</Button>
<Button type="primary">Submit</Button>
</Space>

// Loading States
<Spin spinning={loading}>
{children}
</Spin>

Component Groups

GroupPurposeExamples
Data entryInput componentsTextField, NumberField, Checkbox
Data displayInformation displayText, Alert, Statistic
AdvancedComplex componentsAutocomplete, RichTextEditor
Entity referencesEntity componentsEntityPicker, FileUpload
LayoutPage structureCard, Columns, Tabs
DevDevelopment toolsCodeEditor, JsonEditor

Best Practices

Component Development

✅ Single responsibility
✅ TypeScript interfaces
✅ Responsive design
✅ Meaningful defaults
✅ Performance optimization
✅ Unique IDs using nanoid()

Code Organization

✅ Follow file structure patterns
✅ Reuse utilities
✅ Document complex logic
✅ Use consistent naming

Development Workflow

  1. Create feature branch
  2. Implement component
  3. Register in toolbox
  4. Test in form designer
  5. Commit clearly
  6. Create pull request

Component Lifecycle

Create Component Files

Define Interfaces

Configure Settings

Implement Component

Register in Toolbox

Test in Designer

Deploy & Iterate

Notes on Migrations

Core Principles

  • Don’t modify existing migrations once merged into main
  • Add new versions instead of editing old ones
  • Changing live migrations risks breaking forms and tests

Safe vs Unsafe to Modify

Safe

  • Migrations not merged with main
  • Migrations in local/feature branches

Unsafe

  • Migrations on main
  • Deployed or tested migrations

Proper Migration Strategy

  • Always create a new version for behavior changes
  • Maintain backward compatibility
  • Test before merging
  • Use semantic versioning

Example Scenario

Situation: Add a new default setting
✅ Correct: Create a new migration version
❌ Wrong: Modify old migration or just set defaultValue

Resources

Happy Shesha Development!
Start building amazing configurable components today 🚀