DEV Community

Cover image for JavaScript Form Builder: Master Dynamic Validation, Conditional Logic, and Schema-Driven Architecture
Nithin Bharadwaj
Nithin Bharadwaj

Posted on

JavaScript Form Builder: Master Dynamic Validation, Conditional Logic, and Schema-Driven Architecture

As a best-selling author, I invite you to explore my books on Amazon. Don't forget to follow me on Medium and show your support. Thank you! Your support means the world!

Building dynamic form builders in JavaScript requires understanding several core techniques that work together to create flexible, maintainable systems. I've spent years working with these patterns and have found that success comes from implementing them thoughtfully rather than rushing through each feature.

Component Architecture Design

The foundation of any form builder starts with modular components that can render based on configuration objects. I approach this by creating a registration system where each field type has its own rendering logic and validation rules.

class FormBuilder {
  constructor(container, options = {}) {
    this.container = container;
    this.schema = options.schema || { fields: [] };
    this.fieldTypes = new Map();
    this.validators = new Map();
    this.formData = {};
    this.eventBus = new EventTarget();

    this.init();
  }

  init() {
    this.registerBaseFieldTypes();
    this.setupEventListeners();
  }

  registerFieldType(name, definition) {
    this.fieldTypes.set(name, {
      render: definition.render,
      validate: definition.validate || (() => null),
      serialize: definition.serialize || ((value) => value),
      deserialize: definition.deserialize || ((value) => value)
    });
  }

  registerBaseFieldTypes() {
    this.registerFieldType('text', {
      render: (field, value, onChange) => {
        const input = document.createElement('input');
        input.type = field.inputType || 'text';
        input.value = value || '';
        input.placeholder = field.placeholder || '';
        input.className = 'form-control';

        if (field.attributes) {
          Object.entries(field.attributes).forEach(([key, val]) => {
            input.setAttribute(key, val);
          });
        }

        input.addEventListener('input', (e) => {
          onChange(e.target.value);
          this.triggerFieldEvent('change', field.id, e.target.value);
        });

        return input;
      },
      validate: (value, field) => {
        if (field.required && (!value || value.toString().trim() === '')) {
          return field.requiredMessage || 'This field is required';
        }

        if (value && field.pattern) {
          const regex = new RegExp(field.pattern);
          if (!regex.test(value)) {
            return field.patternMessage || 'Invalid format';
          }
        }

        if (field.minLength && value && value.length < field.minLength) {
          return `Minimum length is ${field.minLength} characters`;
        }

        if (field.maxLength && value && value.length > field.maxLength) {
          return `Maximum length is ${field.maxLength} characters`;
        }

        return null;
      }
    });

    this.registerFieldType('select', {
      render: (field, value, onChange) => {
        const select = document.createElement('select');
        select.className = 'form-control';

        if (field.placeholder) {
          const placeholder = document.createElement('option');
          placeholder.value = '';
          placeholder.textContent = field.placeholder;
          placeholder.disabled = true;
          placeholder.selected = !value;
          select.appendChild(placeholder);
        }

        const options = this.resolveOptions(field);
        options.forEach(option => {
          const optionEl = document.createElement('option');
          optionEl.value = option.value;
          optionEl.textContent = option.label;
          optionEl.selected = value === option.value;
          select.appendChild(optionEl);
        });

        select.addEventListener('change', (e) => {
          onChange(e.target.value);
          this.triggerFieldEvent('change', field.id, e.target.value);
        });

        return select;
      },
      validate: (value, field) => {
        if (field.required && !value) {
          return field.requiredMessage || 'Please select an option';
        }
        return null;
      }
    });
  }

  resolveOptions(field) {
    if (Array.isArray(field.options)) {
      return field.options;
    }

    if (field.optionsSource && this.dataSources) {
      return this.dataSources[field.optionsSource] || [];
    }

    return [];
  }
}
Enter fullscreen mode Exit fullscreen mode

Dynamic Validation Engine

Creating validation systems that adapt to changing form structures requires building rule-based validators that respond to field dependencies. I implement this through a flexible validation engine that can handle complex scenarios.

class ValidationEngine {
  constructor() {
    this.rules = new Map();
    this.validators = new Map();
    this.setupBaseValidators();
  }

  setupBaseValidators() {
    this.validators.set('email', {
      validate: (value) => {
        const pattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return pattern.test(value) || 'Please enter a valid email address';
      }
    });

    this.validators.set('phone', {
      validate: (value) => {
        const pattern = /^[\+]?[1-9][\d]{0,15}$/;
        return pattern.test(value.replace(/\s|-|\(|\)/g, '')) || 'Invalid phone number';
      }
    });

    this.validators.set('range', {
      validate: (value, params) => {
        const num = parseFloat(value);
        if (isNaN(num)) return 'Must be a number';

        if (params.min !== undefined && num < params.min) {
          return `Value must be at least ${params.min}`;
        }

        if (params.max !== undefined && num > params.max) {
          return `Value must be no more than ${params.max}`;
        }

        return true;
      }
    });

    this.validators.set('conditional', {
      validate: (value, params, formData) => {
        const { dependsOn, when, then } = params;
        const dependentValue = formData[dependsOn];

        if (this.evaluateCondition(dependentValue, when)) {
          return this.validateValue(value, then, formData);
        }

        return true;
      }
    });
  }

  addValidationRule(fieldId, rules) {
    this.rules.set(fieldId, rules);
  }

  validateField(fieldId, value, formData = {}) {
    const fieldRules = this.rules.get(fieldId);
    if (!fieldRules) return { valid: true };

    for (const rule of fieldRules) {
      const validator = this.validators.get(rule.type);
      if (!validator) continue;

      const result = validator.validate(value, rule.params, formData);
      if (result !== true) {
        return {
          valid: false,
          message: typeof result === 'string' ? result : 'Validation failed'
        };
      }
    }

    return { valid: true };
  }

  validateForm(formData) {
    const results = {};
    let isValid = true;

    for (const [fieldId] of this.rules) {
      const result = this.validateField(fieldId, formData[fieldId], formData);
      results[fieldId] = result;
      if (!result.valid) isValid = false;
    }

    return { valid: isValid, fieldResults: results };
  }

  evaluateCondition(value, condition) {
    const { operator, value: expectedValue } = condition;

    switch (operator) {
      case 'equals': return value === expectedValue;
      case 'not_equals': return value !== expectedValue;
      case 'contains': return Array.isArray(value) && value.includes(expectedValue);
      case 'not_contains': return !Array.isArray(value) || !value.includes(expectedValue);
      case 'greater_than': return Number(value) > Number(expectedValue);
      case 'less_than': return Number(value) < Number(expectedValue);
      case 'is_empty': return !value || value.toString().trim() === '';
      case 'is_not_empty': return value && value.toString().trim() !== '';
      default: return false;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Conditional Field Logic

Building dependency systems requires creating rule engines that evaluate complex conditions and update form structure in real-time. I handle this through a condition evaluator that manages field visibility and behavior.

class ConditionalLogic {
  constructor(formBuilder) {
    this.formBuilder = formBuilder;
    this.conditions = new Map();
    this.dependencyGraph = new Map();
  }

  addCondition(fieldId, condition) {
    this.conditions.set(fieldId, condition);
    this.buildDependencyGraph();
  }

  buildDependencyGraph() {
    this.dependencyGraph.clear();

    for (const [fieldId, condition] of this.conditions) {
      const dependencies = this.extractDependencies(condition);
      dependencies.forEach(dep => {
        if (!this.dependencyGraph.has(dep)) {
          this.dependencyGraph.set(dep, new Set());
        }
        this.dependencyGraph.get(dep).add(fieldId);
      });
    }
  }

  extractDependencies(condition) {
    const deps = new Set();

    if (condition.dependsOn) {
      if (Array.isArray(condition.dependsOn)) {
        condition.dependsOn.forEach(dep => deps.add(dep));
      } else {
        deps.add(condition.dependsOn);
      }
    }

    if (condition.and) {
      condition.and.forEach(subCondition => {
        this.extractDependencies(subCondition).forEach(dep => deps.add(dep));
      });
    }

    if (condition.or) {
      condition.or.forEach(subCondition => {
        this.extractDependencies(subCondition).forEach(dep => deps.add(dep));
      });
    }

    return Array.from(deps);
  }

  evaluateCondition(condition, formData) {
    if (condition.and) {
      return condition.and.every(subCondition => 
        this.evaluateCondition(subCondition, formData)
      );
    }

    if (condition.or) {
      return condition.or.some(subCondition => 
        this.evaluateCondition(subCondition, formData)
      );
    }

    if (condition.not) {
      return !this.evaluateCondition(condition.not, formData);
    }

    const { dependsOn, operator, value } = condition;
    const fieldValue = formData[dependsOn];

    return this.compareValues(fieldValue, operator, value);
  }

  compareValues(fieldValue, operator, expectedValue) {
    switch (operator) {
      case 'equals':
        return fieldValue === expectedValue;
      case 'not_equals':
        return fieldValue !== expectedValue;
      case 'in':
        return Array.isArray(expectedValue) && expectedValue.includes(fieldValue);
      case 'not_in':
        return !Array.isArray(expectedValue) || !expectedValue.includes(fieldValue);
      case 'contains':
        return Array.isArray(fieldValue) && fieldValue.includes(expectedValue);
      case 'greater_than':
        return Number(fieldValue) > Number(expectedValue);
      case 'less_than':
        return Number(fieldValue) < Number(expectedValue);
      case 'starts_with':
        return typeof fieldValue === 'string' && fieldValue.startsWith(expectedValue);
      case 'ends_with':
        return typeof fieldValue === 'string' && fieldValue.endsWith(expectedValue);
      case 'is_empty':
        return !fieldValue || (Array.isArray(fieldValue) && fieldValue.length === 0);
      case 'is_not_empty':
        return fieldValue && (!Array.isArray(fieldValue) || fieldValue.length > 0);
      default:
        return false;
    }
  }

  onFieldChange(fieldId, value, formData) {
    const affectedFields = this.dependencyGraph.get(fieldId);
    if (!affectedFields) return [];

    const changedFields = [];

    for (const affectedFieldId of affectedFields) {
      const condition = this.conditions.get(affectedFieldId);
      if (condition) {
        const shouldShow = this.evaluateCondition(condition, formData);
        const field = this.formBuilder.getField(affectedFieldId);

        if (field && field.visible !== shouldShow) {
          field.visible = shouldShow;
          changedFields.push(affectedFieldId);

          // Clear value if field becomes hidden
          if (!shouldShow && formData[affectedFieldId] !== undefined) {
            delete formData[affectedFieldId];
          }
        }
      }
    }

    return changedFields;
  }
}
Enter fullscreen mode Exit fullscreen mode

Schema-Driven Generation

Designing JSON schema interpreters that convert configuration objects into functional forms requires creating transformation layers that map abstract definitions to concrete components.

class SchemaInterpreter {
  constructor() {
    this.transformers = new Map();
    this.setupDefaultTransformers();
  }

  setupDefaultTransformers() {
    this.transformers.set('string', (schema) => ({
      type: 'text',
      inputType: schema.format === 'email' ? 'email' : 
                 schema.format === 'password' ? 'password' : 'text',
      required: schema.required || false,
      minLength: schema.minLength,
      maxLength: schema.maxLength,
      pattern: schema.pattern,
      placeholder: schema.placeholder
    }));

    this.transformers.set('number', (schema) => ({
      type: 'text',
      inputType: 'number',
      required: schema.required || false,
      min: schema.minimum,
      max: schema.maximum,
      step: schema.multipleOf || 'any'
    }));

    this.transformers.set('boolean', (schema) => ({
      type: 'checkbox',
      required: false,
      options: [{
        value: true,
        label: schema.title || 'Yes'
      }]
    }));

    this.transformers.set('array', (schema) => {
      if (schema.items && schema.items.enum) {
        return {
          type: 'checkbox',
          options: schema.items.enum.map(value => ({
            value,
            label: schema.enumNames ? 
              schema.enumNames[schema.items.enum.indexOf(value)] : value
          }))
        };
      }

      return {
        type: 'dynamic-list',
        itemSchema: schema.items
      };
    });
  }

  interpretSchema(schema) {
    const form = {
      title: schema.title,
      description: schema.description,
      fields: []
    };

    if (schema.properties) {
      Object.entries(schema.properties).forEach(([key, fieldSchema]) => {
        const field = this.transformField(key, fieldSchema, schema);
        if (field) {
          form.fields.push(field);
        }
      });
    }

    return form;
  }

  transformField(id, schema, parentSchema) {
    const transformer = this.transformers.get(schema.type);
    if (!transformer) {
      console.warn(`No transformer found for type: ${schema.type}`);
      return null;
    }

    const baseField = transformer(schema);

    return {
      id,
      label: schema.title || this.humanizeKey(id),
      description: schema.description,
      required: parentSchema.required && parentSchema.required.includes(id),
      ...baseField
    };
  }

  humanizeKey(key) {
    return key
      .replace(/([A-Z])/g, ' $1')
      .replace(/^./, str => str.toUpperCase())
      .replace(/[_-]/g, ' ')
      .trim();
  }

  addTransformer(type, transformer) {
    this.transformers.set(type, transformer);
  }
}
Enter fullscreen mode Exit fullscreen mode

Field Registration System

Implementing field registration patterns requires centralized state management that handles nested objects and array fields efficiently while tracking all form state changes.

class FieldRegistry {
  constructor() {
    this.fields = new Map();
    this.fieldOrder = [];
    this.observers = new Set();
    this.values = {};
    this.errors = {};
  }

  register(fieldConfig) {
    const { id } = fieldConfig;

    if (this.fields.has(id)) {
      console.warn(`Field ${id} is already registered`);
      return;
    }

    this.fields.set(id, {
      ...fieldConfig,
      registered: true,
      element: null
    });

    this.fieldOrder.push(id);
    this.notifyObservers('register', { fieldId: id, config: fieldConfig });
  }

  unregister(fieldId) {
    if (!this.fields.has(fieldId)) return;

    this.fields.delete(fieldId);
    this.fieldOrder = this.fieldOrder.filter(id => id !== fieldId);
    delete this.values[fieldId];
    delete this.errors[fieldId];

    this.notifyObservers('unregister', { fieldId });
  }

  updateField(fieldId, updates) {
    const field = this.fields.get(fieldId);
    if (!field) return;

    const updatedField = { ...field, ...updates };
    this.fields.set(fieldId, updatedField);

    this.notifyObservers('update', { fieldId, updates, field: updatedField });
  }

  setValue(fieldId, value, options = {}) {
    const field = this.fields.get(fieldId);
    if (!field) return;

    const oldValue = this.values[fieldId];
    this.values[fieldId] = value;

    if (!options.silent) {
      this.notifyObservers('valueChange', {
        fieldId,
        value,
        oldValue,
        field
      });
    }
  }

  getValue(fieldId) {
    return this.values[fieldId];
  }

  getAllValues() {
    return { ...this.values };
  }

  setError(fieldId, error) {
    const oldError = this.errors[fieldId];

    if (error) {
      this.errors[fieldId] = error;
    } else {
      delete this.errors[fieldId];
    }

    this.notifyObservers('errorChange', {
      fieldId,
      error,
      oldError
    });
  }

  getError(fieldId) {
    return this.errors[fieldId];
  }

  getAllErrors() {
    return { ...this.errors };
  }

  hasErrors() {
    return Object.keys(this.errors).length > 0;
  }

  getField(fieldId) {
    return this.fields.get(fieldId);
  }

  getAllFields() {
    return this.fieldOrder.map(id => this.fields.get(id));
  }

  observe(callback) {
    this.observers.add(callback);
    return () => this.observers.delete(callback);
  }

  notifyObservers(event, data) {
    this.observers.forEach(callback => {
      try {
        callback(event, data);
      } catch (error) {
        console.error('Observer error:', error);
      }
    });
  }

  reset() {
    this.values = {};
    this.errors = {};
    this.notifyObservers('reset', {});
  }

  setValues(values) {
    Object.entries(values).forEach(([fieldId, value]) => {
      if (this.fields.has(fieldId)) {
        this.setValue(fieldId, value, { silent: true });
      }
    });

    this.notifyObservers('bulkValueChange', { values });
  }
}
Enter fullscreen mode Exit fullscreen mode

Custom Field Types

Creating extensible architectures requires building plugin systems that allow developers to register new field types with their own validation and rendering logic.

class CustomFieldManager {
  constructor() {
    this.customTypes = new Map();
    this.templates = new Map();
    this.hooks = new Map();
  }

  registerFieldType(name, definition) {
    if (this.customTypes.has(name)) {
      console.warn(`Field type ${name} already exists and will be overwritten`);
    }

    const fieldType = {
      name,
      render: definition.render,
      validate: definition.validate || (() => null),
      serialize: definition.serialize || ((value) => value),
      deserialize: definition.deserialize || ((value) => value),
      defaultProps: definition.defaultProps || {},
      hooks: definition.hooks || {}
    };

    this.customTypes.set(name, fieldType);
    this.registerHooks(name, fieldType.hooks);

    return fieldType;
  }

  createCustomField(type, config, value, onChange) {
    const fieldType = this.customTypes.get(type);
    if (!fieldType) {
      throw new Error(`Unknown field type: ${type}`);
    }

    const mergedConfig = {
      ...fieldType.defaultProps,
      ...config
    };

    const element = fieldType.render(mergedConfig, value, onChange);
    this.executeHook(type, 'afterRender', { element, config: mergedConfig, value });

    return element;
  }

  registerTemplate(name, template) {
    this.templates.set(name, template);
  }

  getTemplate(name) {
    return this.templates.get(name);
  }

  registerHooks(fieldType, hooks) {
    if (!this.hooks.has(fieldType)) {
      this.hooks.set(fieldType, {});
    }

    const fieldHooks = this.hooks.get(fieldType);
    Object.assign(fieldHooks, hooks);
  }

  executeHook(fieldType, hookName, params) {
    const fieldHooks = this.hooks.get(fieldType);
    if (fieldHooks && fieldHooks[hookName]) {
      try {
        return fieldHooks[hookName](params);
      } catch (error) {
        console.error(`Hook ${hookName} for ${fieldType} failed:`, error);
      }
    }
  }

  // Example custom field types
  setupBuiltInCustomTypes() {
    this.registerFieldType('date-range', {
      render: (config, value, onChange) => {
        const container = document.createElement('div');
        container.className = 'date-range-field';

        const startDate = document.createElement('input');
        startDate.type = 'date';
        startDate.placeholder = 'Start Date';
        startDate.value = value?.start || '';

        const endDate = document.createElement('input');
        endDate.type = 'date';
        endDate.placeholder = 'End Date';
        endDate.value = value?.end || '';

        const updateValue = () => {
          onChange({
            start: startDate.value,
            end: endDate.value
          });
        };

        startDate.addEventListener('change', updateValue);
        endDate.addEventListener('change', updateValue);

        container.appendChild(startDate);
        container.appendChild(document.createTextNode(' to '));
        container.appendChild(endDate);

        return container;
      },
      validate: (value, config) => {
        if (config.required && (!value || !value.start || !value.end)) {
          return 'Both start and end dates are required';
        }

        if (value && value.start && value.end) {
          const start = new Date(value.start);
          const end = new Date(value.end);

          if (start >= end) {
            return 'End date must be after start date';
          }
        }

        return null;
      },
      serialize: (value) => value ? JSON.stringify(value) : '',
      deserialize: (value) => {
        try {
          return value ? JSON.parse(value) : null;
        } catch {
          return null;
        }
      }
    });

    this.registerFieldType('file-upload', {
      render: (config, value, onChange) => {
        const container = document.createElement('div');
        container.className = 'file-upload-field';

        const input = document.createElement('input');
        input.type = 'file';
        input.multiple = config.multiple || false;
        input.accept = config.accept || '';

        const preview = document.createElement('div');
        preview.className = 'file-preview';

        input.addEventListener('change', (e) => {
          const files = Array.from(e.target.files);
          onChange(files);
          this.updateFilePreview(preview, files);
        });

        container.appendChild(input);
        container.appendChild(preview);

        if (value) {
          this.updateFilePreview(preview, Array.isArray(value) ? value : [value]);
        }

        return container;
      },
      validate: (value, config) => {
        if (config.required && (!value || value.length === 0)) {
          return 'Please select a file';
        }

        if (value && config.maxSize) {
          const files = Array.isArray(value) ? value : [value];
          for (const file of files) {
            if (file.size > config.maxSize) {
              return `File size must not exceed ${this.formatFileSize(config.maxSize)}`;
            }
          }
        }

        return null;
      }
    });
  }

  updateFilePreview(container, files) {
    container.innerHTML = '';

    files.forEach(file => {
      const fileItem = document.createElement('div');
      fileItem.className = 'file-item';
      fileItem.textContent = `${file.name} (${this.formatFileSize(file.size)})`;
      container.appendChild(fileItem);
    });
  }

  formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }
}
Enter fullscreen mode Exit fullscreen mode

Performance Optimization

Implementing virtual scrolling and preventing unnecessary re-renders becomes crucial when dealing with forms containing hundreds of fields. I use memoization techniques and efficient DOM updates.


javascript
class PerformanceOptimizer {
  constructor(formBuilder) {
    this.formBuilder = formBuilder;
    this.renderCache = new Map();
    this.debounceTimers = new Map();
    this.virtualScroll = null;
  }

  setupVirtualScrolling(container, itemHeight = 60) {
    this.virtualScroll = new VirtualScroller(container, {
      itemHeight,
      renderItem: (index, field) => this.renderFieldOptimized(field),
      getItemKey: (field) => field.id
    });
  }

  renderFieldOptimized(field) {
    const cacheKey = this.generateCacheKey(field);

    if (this.renderCache.has(cacheKey)) {
      return this.renderCache.get(cacheKey).cloneNode(true);
    }

    const element = this.formBuilder.renderField(field);
    this.renderCache.set(cacheKey, element.cloneNode(true));

    return element;
  }

  generateCacheKey(field) {
    return JSON.stringify({
      type: field.type,
      required: field.required,
      options: field.options,
      placeholder: field.placeholder
    });
  }

  debounceFieldUpdate(fieldId, updateFn, delay = 300) {
    if (this.debounceTimers.has(fieldId)) {
      clearTimeout(this.debounceTimers.get(fieldId));
    }

    const timer = setTimeout(() => {
      updateFn();
      this.debounceTimers.delete(fieldId);
    }, delay);

    this.debounceTimers.set(fieldId, timer);
  }

  batchUpdates(updates) {
    const fragment = document.createDocumentFragment();

    updates.forEach(update => {
      const element = this.processUpdate(update);
      if (element) {
        fragment.appendChild(element);
      }
    });

    return fragment;
  }

  processUpdate(update) {
    switch (update.type) {
      case 'add':
        return this.renderFieldOptimized(update.field);
      case 'remove':
        const element = document.querySelector(`[data-field-id="${update.fieldId}"]`);
        if (element) element.remove();
        return null;
      case 'update':
        return this.updateFieldInPlace(update.fieldId, update.changes);
      default:
        return null;
    }
  }

  updateFieldInPlace(fieldId, changes) {
    const element = document.querySelector(`[data-field-id="${fieldId}"]`);
    if (!element) return null;

    Object.entries(changes).forEach(([key, value]) => {
      switch (key) {
        case 'visible':
          element.style.display = value ? 'block' : 'none';
          break;
        case 'required':
          const input = element.querySelector('input, select, textarea');
          if (input) input.required = value;
          break;
        case 'disabled':
          const controls = element.querySelectorAll('input, select, textarea, button');
          controls.forEach(control => control.disabled = value);
          break;
      }
    });

    return element;
  }

  clearCache() {
    this.renderCache.clear();
  }

  cleanup() {
    this.debounceTimers.forEach(timer => clearTimeout(timer));
    this.debounceTimers.clear();
    this.clearCache();
  }
}

class VirtualScroller {
  constructor(container, options) {
    this.container = container;
    this.options = options;
    this.items = [];
    this.visibleItems = [];
    this.scrollTop = 0;
    this.containerHeight = 0;

    this.setupScrolling();
  }

  setupScrolling() {
    this.container.style.overflowY = 'auto';
    this.container.addEventListener('scroll', ()
---
📘 **Checkout my [latest ebook](https://youtu.be/WpR6F4ky4uM) for free on my channel!**  
Be sure to **like**, **share**, **comment**, and **subscribe** to the channel!

---
## 101 Books

**101 Books** is an AI-driven publishing company co-founded by author **Aarav Joshi**. By leveraging advanced AI technology, we keep our publishing costs incredibly low—some books are priced as low as **$4**—making quality knowledge accessible to everyone.

Check out our book **[Golang Clean Code](https://www.amazon.com/dp/B0DQQF9K3Z)** available on Amazon. 

Stay tuned for updates and exciting news. When shopping for books, search for **Aarav Joshi** to find more of our titles. Use the provided link to enjoy **special discounts**!

## Our Creations

Be sure to check out our creations:

**[Investor Central](https://www.investorcentral.co.uk/)** | **[Investor Central Spanish](https://spanish.investorcentral.co.uk/)** | **[Investor Central German](https://german.investorcentral.co.uk/)** | **[Smart Living](https://smartliving.investorcentral.co.uk/)** | **[Epochs & Echoes](https://epochsandechoes.com/)** | **[Puzzling Mysteries](https://www.puzzlingmysteries.com/)** | **[Hindutva](http://hindutva.epochsandechoes.com/)** | **[Elite Dev](https://elitedev.in/)** | **[JS Schools](https://jsschools.com/)**

---

### We are on Medium

**[Tech Koala Insights](https://techkoalainsights.com/)** | **[Epochs & Echoes World](https://world.epochsandechoes.com/)** | **[Investor Central Medium](https://medium.investorcentral.co.uk/)** | **[Puzzling Mysteries Medium](https://medium.com/puzzling-mysteries)** | **[Science & Epochs Medium](https://science.epochsandechoes.com/)** | **[Modern Hindutva](https://modernhindutva.substack.com/)**

Enter fullscreen mode Exit fullscreen mode

Top comments (0)