# Backend-Driven Form Rendering with JSON Schema in Formily: Complete Implementation Guide

> Master backend-driven form rendering with JSON Schema in Formily. Let your server build dynamic forms, saving frontend code updates for UI, validation, and layout changes.

- Repository: [Alibaba/formily](https://github.com/alibaba/formily)
- Tags: how-to-guide
- Published: 2026-02-24

---

**Backend-driven form rendering with JSON Schema in Formily allows your server to send a declarative payload that the framework automatically converts into a fully functional form UI, enabling you to modify fields, validation rules, and layout without touching frontend code.**

The **alibaba/formily** repository provides a schema-driven form framework that treats forms as pure data structures. By leveraging **backend-driven form rendering with JSON Schema in Formily**, you create a strict separation between your data layer and presentation layer—the backend defines the form shape, while the frontend simply renders what it receives. This architecture enables rapid iteration through centralized form management and eliminates the need for redeploying client applications when business requirements change.

## How Backend-Driven Rendering Works in Formily

Formily implements a four-stage pipeline that transforms a raw JSON Schema into interactive UI components. Understanding this flow is essential for debugging and extending schema-driven forms.

### The JSON Schema to Formily Schema Pipeline

When your backend returns a JSON Schema, Formily's `@formily/json-schema` package immediately begins processing. The **transformer** located in [`packages/json-schema/src/transformer.ts`](https://github.com/alibaba/formily/blob/main/packages/json-schema/src/transformer.ts) traverses the schema tree to normalize the structure and resolve `$ref` pointers. During this walk, it merges default values and attaches Formily-specific extensions including `x-component`, `x-decorator`, and `x-reactions` to each node. The output is a structured **Schema** object defined in [`packages/json-schema/src/schema.ts`](https://github.com/alibaba/formily/blob/main/packages/json-schema/src/schema.ts), which provides instance methods like `addProperty` and `setItems` for runtime manipulation.

### The Rendering Architecture

After transformation, the frontend creates a **Form** instance using `createForm()` from [`packages/core/src/models/Form.ts`](https://github.com/alibaba/formily/blob/main/packages/core/src/models/Form.ts). This central model holds form state, field values, validation status, and side effects. The **SchemaField** component—implemented in [`packages/vue/src/components/SchemaField.ts`](https://github.com/alibaba/formily/blob/main/packages/vue/src/components/SchemaField.ts) for Vue or [`packages/react/src/components/SchemaField.tsx`](https://github.com/alibaba/formily/blob/main/packages/react/src/components/SchemaField.tsx) for React—injects the parsed schema into the component tree via framework-specific context providers. Finally, **RecursionField** ([`packages/vue/src/components/RecursionField.ts`](https://github.com/alibaba/formily/blob/main/packages/vue/src/components/RecursionField.ts)) recursively walks the schema tree, rendering each leaf node as its designated component (e.g., `SchemaStringField`) based on the `x-component` mappings.

## Implementation: From Backend to Frontend

Implementing backend-driven forms requires coordination between your API layer and Formily's component registration system. The following steps demonstrate the complete data flow from server to browser.

### Step 1: Serving the Schema from Your Backend

Your backend must expose an endpoint that returns a valid JSON Schema extended with Formily properties. These extensions use the `x-` prefix to specify which UI components should render each field.

```typescript
// server.ts - Express example
import express from 'express';
const app = express();

app.get('/api/form-schema', (req, res) => {
  res.json({
    type: 'object',
    properties: {
      username: {
        type: 'string',
        title: 'User Name',
        'x-component': 'Input',
        required: true,
      },
      age: {
        type: 'number',
        title: 'Age',
        'x-component': 'NumberPicker',
        minimum: 0,
      },
      agree: {
        type: 'boolean',
        title: 'Agree to Terms',
        'x-component': 'Checkbox',
        'x-decorator': 'FormItem',
      },
    },
  });
});

app.listen(3000);

```

### Step 2: Converting JSON Schema to Internal Schema Objects

When the frontend fetches this payload, Formily automatically processes it through the transformer pipeline. You do not manually instantiate the **Schema** class; instead, you pass the raw JSON directly to the rendering components, which internally utilize [`packages/json-schema/src/transformer.ts`](https://github.com/alibaba/formily/blob/main/packages/json-schema/src/transformer.ts) to handle normalization and extension merging.

### Step 3: Rendering with SchemaField Components

The **SchemaField** component acts as the bridge between your schema data and your registered UI components. You must first register your actual input components (Input, Checkbox, etc.) using `createSchemaField()` before the schema can render.

## Code Examples

### Vue 3 Implementation

In Vue applications, you combine `createForm()` with `FormProvider` and the schema-enabled **SchemaField** component. Fetch the schema in your setup function and conditionally render once loaded.

```vue
<script setup lang="ts">
import { createForm } from '@formily/core';
import { createSchemaField, FormProvider } from '@formily/vue';
import { onMounted, ref } from 'vue';

const form = createForm();

const { SchemaField } = createSchemaField({
  components: {
    Input: /* your Input component */,
    NumberPicker: /* your NumberPicker component */,
    Checkbox: /* your Checkbox component */,
    FormItem: /* your FormItem decorator */,
  },
});

const schema = ref(null);
onMounted(async () => {
  const resp = await fetch('/api/form-schema');
  schema.value = await resp.json();
});
</script>

<template>
  <FormProvider :form="form">
    <SchemaField v-if="schema" :schema="schema" />
  </FormProvider>
</template>

```

### React Implementation

The React implementation follows an identical pattern, using hooks to fetch the remote schema and `FormProvider` to inject the form instance context.

```tsx
import { createForm } from '@formily/core';
import { createSchemaField, FormProvider } from '@formily/react';
import { useEffect, useState } from 'react';

const form = createForm();

const { SchemaField } = createSchemaField({
  components: {
    Input: (props) => <input {...props} />,
    NumberPicker: (props) => <input type="number" {...props} />,
    Checkbox: (props) => <input type="checkbox" {...props} />,
  },
});

export const RemoteForm = () => {
  const [schema, setSchema] = useState<any>(null);

  useEffect(() => {
    fetch('/api/form-schema')
      .then((res) => res.json())
      .then(setSchema);
  }, []);

  return (
    <FormProvider form={form}>
      {schema && <SchemaField schema={schema} />}
    </FormProvider>
  );
};

```

## Key Source Files in the Formily Repository

Understanding the internal architecture helps when debugging complex schema transformations or extending the framework's capabilities:

- **[`packages/json-schema/src/transformer.ts`](https://github.com/alibaba/formily/blob/main/packages/json-schema/src/transformer.ts)** — Core transformation logic that converts raw JSON Schema into Formily's internal representation, handling `$ref` resolution and extension merging.

- **[`packages/json-schema/src/schema.ts`](https://github.com/alibaba/formily/blob/main/packages/json-schema/src/schema.ts)** — Implements the **Schema** class with methods for property manipulation and schema traversal used throughout the runtime.

- **[`packages/core/src/models/Form.ts`](https://github.com/alibaba/formily/blob/main/packages/core/src/models/Form.ts)** — Defines the **Form** model that manages state, values, validation, and side effects for the entire form instance.

- **[`packages/vue/src/components/SchemaField.ts`](https://github.com/alibaba/formily/blob/main/packages/vue/src/components/SchemaField.ts)** — Vue-specific component that provides schema context and initiates the recursive rendering process.

- **[`packages/vue/src/components/RecursionField.ts`](https://github.com/alibaba/formily/blob/main/packages/vue/src/components/RecursionField.ts)** — Handles the recursive rendering of nested schema properties by mapping each node to its registered component based on `x-component` values.

- **[`packages/react/src/components/SchemaField.tsx`](https://github.com/alibaba/formily/blob/main/packages/react/src/components/SchemaField.tsx)** — React equivalent of the Vue SchemaField, managing context injection for React-based applications.

## Summary

- **Backend-driven form rendering** moves form definitions from frontend code to server-side data, enabling instant updates without redeployment.
- The **[`transformer.ts`](https://github.com/alibaba/formily/blob/main/transformer.ts)** file in `@formily/json-schema` handles the heavy lifting of converting JSON Schema into Formily's internal **Schema** objects.
- **SchemaField** components in both Vue and React act as the entry point for rendering, while **RecursionField** manages nested structures.
- Form extensions like **`x-component`** and **`x-decorator`** allow you to specify UI behavior directly within your JSON Schema.
- The **Form** model ([`packages/core/src/models/Form.ts`](https://github.com/alibaba/formily/blob/main/packages/core/src/models/Form.ts)) maintains all runtime state independently of the rendering layer.

## Frequently Asked Questions

### What is the role of the transformer.ts file in Formily?

The [`transformer.ts`](https://github.com/alibaba/formily/blob/main/transformer.ts) file in `packages/json-schema/src/` serves as the core engine that parses incoming JSON Schema payloads. It traverses the schema tree to resolve `$ref` references, normalize property structures, and merge Formily-specific extensions (like `x-component` and `x-reactions`) into a cohesive **Schema** instance that the rendering engine can consume.

### How does Formily handle component registration for schema-driven forms?

Formily requires you to register your UI components explicitly using `createSchemaField()` before rendering. This function maps string identifiers used in the `x-component` property (such as "Input" or "Checkbox") to actual Vue or React components. The **SchemaField** component then looks up these mappings during the recursive rendering process handled by **RecursionField**.

### Can Formily render nested or complex JSON Schema structures?

Yes. Formily's **RecursionField** component ([`packages/vue/src/components/RecursionField.ts`](https://github.com/alibaba/formily/blob/main/packages/vue/src/components/RecursionField.ts) or React equivalent) automatically handles nested objects and arrays within your JSON Schema. It walks the entire schema tree depth-first, rendering each node according to its type and `x-component` definition, making it suitable for deeply nested forms without manual iteration.

### What happens when the backend updates the JSON Schema?

When the backend modifies the schema—adding fields, changing validation rules, or updating layouts—the frontend automatically reflects these changes on the next fetch request. Because the UI is derived entirely from the schema data, no frontend code changes or redeployment are required. The **transformer** processes the new structure immediately, and **SchemaField** renders the updated component tree.