{"id":19493946,"url":"https://github.com/darianstlex/efx-forms","last_synced_at":"2026-05-26T02:12:04.511Z","repository":{"id":57220244,"uuid":"440507898","full_name":"darianstlex/efx-forms","owner":"darianstlex","description":"Effector Forms Library","archived":false,"fork":false,"pushed_at":"2026-05-22T21:58:09.000Z","size":640,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-05-22T22:57:36.998Z","etag":null,"topics":["effector","form","form-validation","forms","react","validation"],"latest_commit_sha":null,"homepage":"https://darianstlex.github.io/efx-forms/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/darianstlex.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2021-12-21T12:23:23.000Z","updated_at":"2026-05-22T22:00:08.000Z","dependencies_parsed_at":"2024-08-05T17:26:43.684Z","dependency_job_id":null,"html_url":"https://github.com/darianstlex/efx-forms","commit_stats":null,"previous_names":[],"tags_count":60,"template":false,"template_full_name":null,"purl":"pkg:github/darianstlex/efx-forms","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darianstlex%2Fefx-forms","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darianstlex%2Fefx-forms/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darianstlex%2Fefx-forms/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darianstlex%2Fefx-forms/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/darianstlex","download_url":"https://codeload.github.com/darianstlex/efx-forms/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/darianstlex%2Fefx-forms/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33499282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-25T14:31:05.219Z","status":"ssl_error","status_checked_at":"2026-05-25T14:31:02.878Z","response_time":57,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["effector","form","form-validation","forms","react","validation"],"created_at":"2024-11-10T21:27:57.226Z","updated_at":"2026-05-26T02:12:04.498Z","avatar_url":"https://github.com/darianstlex.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EFX-Forms\n\n\u003e Effector JS forms\n\n📚 **Documentation**: https://darianstlex.github.io/efx-forms/\n\n## Installation\n```bash\n$ npm install efx-forms\n```\nPeer dependencies - library depends on:\n\u003e react effector effector-react lodash\n\n## Main Components\n\n### Form / Field\n```jsx\nimport { Form, Field } from 'efx-forms';\nimport { FormDataProvider } from 'efx-forms/FormDataProvider';\nimport { required, email } from 'efx-forms/validators';\n\nconst Input = ({ id, label, error, errors, value, ...props }) =\u003e (\n  \u003cdiv\u003e\n    \u003clabel htmlFor={id}\u003e{label}\u003c/label\u003e\n    \u003cinput id={id} value={value || ''} type=\"text\" {...props} /\u003e\n    \u003cspan\u003e{error}\u003c/span\u003e\n  \u003c/div\u003e\n)\n\nconst TextField = (props) =\u003e \u003cField Field={Input} {...props} /\u003e\n\nconst validators = {\n  name: [required()],\n}\n\nconst Page = () =\u003e {\n  const submit = (values) =\u003e {\n    console.log(values);\n  }\n  return (\n    \u003cForm name=\"user-form\" onSubmit={submit} validators={validators}\u003e\n      \u003cTextField name=\"name\" label=\"Name\" /\u003e\n      \u003cTextField\n        name=\"email\"\n        label=\"Email\"\n        type=\"email\"\n        validators={[\n          required({ msg: `Hey, email is required` }),\n          email(),\n        ]}\n      /\u003e\n      {[0, 1, 2].map((idx) =\u003e (\n        \u003cTextField\n          key={idx}\n          name={`address[${idx}]`}\n          label={`Address ${idx + 1}`}\n        /\u003e\n      ))}\n      \u003cFormDataProvider\u003e\n        {({ values }) =\u003e (\n          \u003cdiv\u003e\n            \u003cpre\u003eJSON.stringify(values)\u003c/pre\u003e\n            \u003cpre\u003eJSON.stringify(shapeFy(values))\u003c/pre\u003e\n          \u003c/div\u003e\n        )}\n      \u003c/FormDataProvider\u003e\n      \u003cbutton type=\"submit\"\u003eSubmit\u003c/button\u003e\n    \u003c/Form\u003e\n  )\n}\n```\n```ts\n// Form values\nvalues = {\n  'name': 'John',\n  'email': 'john@test.com',\n  'address[0]': 'First Line',\n  'address[1]': 'Second Line',\n  'address[2]': 'Postcode',\n}\nvaluesShape = {\n  'name': 'John',\n  'email': 'john@test.com',\n  'address': [\n    'First Line',\n    'Second Line',\n    'Postcode',\n  ]\n}\n```\n\n# Props\n### Form component\n```ts\ninterface Form {\n  // Form name - required, used to get form instance outside of context\n  name: string,\n  /**\n   * Form submit method - on validation success will be called with\n   * form values.\n   * If skipClientValidation is set - no validation will be applied.\n   * If submit return promise:\n   * - reject - object with errors per field - errors will **replace** all existing errors\n   *   (uses replaceErrors, not setErrors - client validation errors are cleared)\n   *   { 'user.name': 'Name is already taken', ... }\n   * - resolve - success submit\n   * @param values - IRValues - flat\n   * @example\n   * { 'user.name': 'John', 'user.age': '20' }\n   */\n  onSubmit?: (values: IRValues) =\u003e void | Promise\u003cIRErrors\u003e;\n  // If set, submit will skip client form validation\n  // Default: false\n  skipClientValidation?: boolean;\n  // Form initial values - field initialValue is in priority\n  initialValues?: { fieldName: 'value' }\n  // Keep form data on unmount\n  // Default: false\n  keepOnUnmount: boolean;\n  // PROPERTY - serialize - serialize stores\n  // not reactive, initialized only once on form creation\n  // Default: false\n  serialize?: boolean;\n  // Set fields validation behavior onBlur\n  // Default: true\n  validateOnBlur?: boolean;\n  // Set fields validation behavior onChange\n  // Default: false\n  validateOnChange?: boolean;\n  // Disable reinit on initialValue change\n  disableFieldsReinit?: boolean;\n  // Validators config per field - field validators are in priority\n  validators?: {\n    fieldName: [\n      (value: any, values: Record\u003cstring, any\u003e) =\u003e string | false,\n    ]\n  };\n}\n```\n\n### Field component\n```ts\ninterface Field {\n  // Field name - required, used to register/get field in the form\n  name: string,\n  // Field initial value - used on initial load and reset\n  // default = ''\n  initialValue?: any;\n  // Transform value before set to store\n  parse?: (value: any) =\u003e any;\n  // Format value before displaying\n  format?: (value: any) =\u003e any;\n  // Passive field does not update its active state and config\n  passive?: boolean;\n  // Validators array - applied on validation\n  validators?: TFieldValidator[];\n  // Set validation behaviour onBlur, overrides form value\n  // Default: true\n  validateOnBlur?: boolean;\n  // Set validation behaviour onChange, overrides form value\n  // Default: false\n  validateOnChange?: boolean;\n  // Disable reinit on initialValue change\n  disableFieldReinit?: boolean;\n  // Field component - component to be used as form field\n  Field: ReactComponent\u003cany\u003e;\n  // Form name - if field belongs to a different form or used outside\n  // of the form context\n  formName?: string;\n}\n```\n\n### IfFormValues component\nConditional rendering based on form values\n```ts\ninterface IfFormValues {\n  children?: ReactNode;\n  // Form name - used to get form values,\n  // if not provided will be taken from context\n  form?: string;\n  // Condition check - accepts form values and return boolean,\n  // if true render children\n  check: (values: IRValues, activeValues: IRValues) =\u003e boolean;\n  // Set fields values on show - { fieldName: 'value' }\n  setTo?: IRValues;\n  // Set fields values on hide - { fieldName: 'value' }\n  resetTo?: IRValues;\n  // Debounce for fields update\n  // Default: 0\n  updateDebounce?: number;\n  // Render prop - accepts form values and return react element\n  // if defined will be used instead of children\n  render?: (values: IRValues) =\u003e ReactElement;\n}\n```\n\n```jsx\nimport { IfFormValues } from 'efx-forms/IfFormValues';\n\nconst ConditionalRender = () =\u003e (\n  \u003cIfFormValues check={({ age }) =\u003e age \u003e 21 }\u003e\n    \u003cdiv\u003eHey, I am here\u003c/div\u003e\n  \u003c/IfFormValues\u003e\n);\n\nconst ConditionalRenderProp = () =\u003e (\n  \u003cIfFormValues\n    check={({ age }) =\u003e age \u003e 21 }\n    render={({ age, name }) =\u003e \u003cdiv\u003eHi, I am {name} - {age}\u003c/div\u003e}\n  /\u003e\n);\n```\n\n### FormDataProvider component\nSubscribe for form values changes\n```ts\ninterface FormDataProvider {\n  // Render function - provides all subscribed data\n  children: (values: ReturnType\u003ctypeof useFormData\u003e) =\u003e ReactNode;\n  // Form name if used outside of context or refers to another form\n  name?: string;\n}\n```\n```jsx\nimport { FormDataProvider } from 'efx-forms/FormDataProvider';\n\nconst FormData = () =\u003e (\n  \u003cFormDataProvider\u003e\n    {({ values, errors }) =\u003e \u003cdiv\u003e{values} - {errors}\u003c/div\u003e}\n  \u003c/FormDataProvider\u003e\n);\n```\n\n### IfFieldValue component\nConditional rendering based on field value\n```ts\ninterface IfFieldValue {\n  children?: ReactNode;\n  // Field name\n  field: string;\n  // Form name - used to get form values,\n  // if not provided will be taken from context\n  formName?: string;\n  // Condition check - accepts form values and return boolean,\n  // if true render children\n  check: (value: any) =\u003e boolean;\n  // Render prop - accepts form values and return react element\n  // if defined will be used instead of children\n  render?: (values: any) =\u003e ReactElement;\n}\n```\n\n```jsx\nimport { IfFieldValue } from 'efx-forms/IfFieldValue';\n\nconst ConditionalRender = () =\u003e (\n  \u003cIfFieldValue check={(age) =\u003e age \u003e 21 }\u003e\n    \u003cdiv\u003eHey, I am here\u003c/div\u003e\n  \u003c/IfFieldValue\u003e\n);\n\nconst ConditionalRenderProp = () =\u003e (\n  \u003cIfFieldValue\n    check={(age) =\u003e age \u003e 21 }\n    render={(age) =\u003e \u003cdiv\u003eHi, I am {age}\u003c/div\u003e}\n  /\u003e\n);\n```\n\n### FieldDataProvider component\nSubscribe for field value changes\n\n```ts\ninterface FieldDataProvider {\n  // Render function - provides all subscribed data\n  children: (values: ReturnType\u003ctypeof useFieldData\u003e) =\u003e ReactNode;\n  // Field name to get stores values from\n  name: string;\n  // Form name if used outside of context or refers to another form\n  formName?: string;\n}\n```\n```jsx\nimport { FieldDataProvider } from 'efx-forms/FieldDataProvider';\n\nconst FieldData = () =\u003e (\n  \u003cFieldDataProvider name=\"user.name\"\u003e\n    {({ value, active }) =\u003e \u003cdiv\u003e{value} - {active}\u003c/div\u003e}\n  \u003c/FieldDataProvider\u003e\n);\n```\n\n# Instances\nForm Instance\n```ts\ninterface FormInstance {\n  /** PROPERTY - Form name */\n  domain: Domain;\n  /** PROPERTY - Form name */\n  name: string;\n  /** $$STORE - Form active fields - all fields statuses - flat */\n  $active: Store\u003cRecord\u003cstring, boolean\u003e\u003e;\n  /** $$STORE - Form active only fields - flat */\n  $activeOnly: Store\u003cRecord\u003cstring, true\u003e\u003e;\n  /** $$STORE - Form active values - all active / visible fields values - flat */\n  $activeValues: Store\u003cIRValues\u003e;\n  /** $$STORE - Form values - all fields values - flat */\n  $values: Store\u003cIRValues\u003e;\n  /** $$STORE - Form errors - all field errors */\n  $errors: Store\u003cIRErrors\u003e;\n  /** $$STORE - Form errors - fields last error - flat */\n  $error: Store\u003cIRError\u003e;\n  /** $$STORE - Form valid - true if form is valid */\n  $valid: Store\u003cboolean\u003e;\n  /** $$STORE - Form submitting - true if busy */\n  $submitting: Store\u003cboolean\u003e;\n  /** $$STORE - Form touched - true if touched */\n  $touched: Store\u003cboolean\u003e;\n  /** $$STORE - Form touches - all fields touches - flat */\n  $touches: Store\u003cRecord\u003cstring, boolean\u003e\u003e;\n  /** $$STORE - Form dirty - true if diff from initial value */\n  $dirty: Store\u003cboolean\u003e;\n  /** $$STORE - Form dirties - all fields dirty state - flat */\n  $dirties: Store\u003cRecord\u003cstring, boolean\u003e\u003e;\n  /** PROP - Form config */\n  config: IFormConfig;\n  /** PROP - Form config */\n  configs: Record\u003cstring, IFieldConfig\u003e;\n  /** EVENT - Form erase - reset form and delete all assigned form data */\n  erase: EventCallable\u003cvoid\u003e;\n  /** EVENT - Form onChange event */\n  onChange: EventCallable\u003c{ name: string; value: any; }\u003e;\n  /** EVENT - Form onBlur event */\n  onBlur: EventCallable\u003c{ name: string; value: any; }\u003e;\n  /** EVENT - Form reset - resets form to initial values */\n  reset: EventCallable\u003cvoid\u003e;\n  /** EVENT - Field reset - resets field to initial value */\n  resetField: EventCallable\u003cstring\u003e;\n  /** EVENT - Reset untouched fields to initial values */\n  resetUntouched: EventCallable\u003cstring[]\u003e;\n  /** EVENT - Set form config */\n  setActive: EventCallable\u003c{ name: string; value: boolean; }\u003e;\n  /** EVENT - Set form config */\n  setConfig: EventCallable\u003cIFormConfig\u003e;\n  /** EVENT - Set field config */\n  setFieldConfig: EventCallable\u003cIFieldConfig\u003e;\n  /** EVENT - Form update field values */\n  setValues: EventCallable\u003cIRValues\u003e;\n  /** EVENT - Form merge errors - merges provided errors into existing $errors store */\n  setErrors: EventCallable\u003cIRErrors\u003e;\n  /** EVENT - Form replace errors - replaces all $errors with provided errors (useful for server validation) */\n  replaceErrors: EventCallable\u003cIRErrors\u003e;\n  /**\n   * EFFECT - Form submit - callback will be called with form values if form is valid\n   * or if callback returns promise reject with errors, will highlight them in the form\n   */\n  submit: Effect\u003cISubmitArgs, ISubmitResponseSuccess, ISubmitResponseError\u003e;\n  /** EVENT - Form validate trigger */\n  validate: EventCallable\u003cIValidationParams\u003e;\n}\n\n/**\n * setErrors vs replaceErrors:\n * - setErrors: Merges errors into existing $errors (preserves unrelated field errors)\n * - replaceErrors: Completely replaces $errors with new errors (clears all existing errors)\n *\n * **Important**: Submit validation uses `replaceErrors` - server errors from `onSubmit` reject\n * will **replace** all client validation errors, not merge with them.\n *\n * Example:\n *   // Current errors: { name: ['Required'] }\n *   setErrors({ email: ['Invalid'] })\n *   // Result: { name: ['Required'], email: ['Invalid'] }\n *\n *   replaceErrors({ email: ['Invalid'] })\n *   // Result: { email: ['Invalid'] }\n *\n *   // Submit reject (uses replaceErrors):\n *   onSubmit: async (values) =\u003e {\n *     throw { email: 'Already exists' }; // Clears name error, only shows email\n *   }\n *\n * Use cases:\n * - setErrors: Add field errors without clearing others (e.g., adding server errors to existing client errors)\n * - replaceErrors: Server validation response (clear all client errors, show only server errors) - **used by submit**\n */\n```\n\n# Methods / Hooks\n```ts\nimport { getForm, useFormInstance } from 'efx-forms';\nimport { useForm } from 'efx-forms/useForm';\nimport { useFormData } from 'efx-forms/useFormData';\nimport { useFormValues } from 'efx-forms/useFormValues';\nimport { useFormStore } from 'efx-forms/useFormStore';\nimport { useFormStores } from 'efx-forms/useFormStores';\nimport { useFormMethods } from 'efx-forms/useFormMethods';\nimport { useField } from 'efx-forms/useField';\nimport { useFieldData } from 'efx-forms/useFieldData';\nimport { useFieldStore } from 'efx-forms/useFieldStore';\nimport { useFieldMethods } from 'efx-forms/useFieldMethods';\nimport { useStoreProp } from 'efx-forms/useStoreProp';\n\n/**\n * Return form by name\n * @type (config: IFormConfig) =\u003e IForm\n */\nconst formOne = getForm({ name: 'form-one' });\n\n/**\n * Hook - return form (from context) data/methods or provided form by name.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * Result includes all form data in plain objects and units in scope\n * @type (formName?: string) =\u003e ReturnType\u003ctypeof useForm\u003e\n */\nconst formTwo = useForm();\n\n/**\n * Hook - return form (from context) data or provided form by name.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * Result includes all form data in plain objects and units in scope\n * @type (formName?: string) =\u003e ReturnType\u003ctypeof useFormData\u003e\n */\nconst formThree = useFormData();\n\n/**\n * Hook - return form (from context) instance or provided form by name.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * Result contains all form stores and units, use useUnit to get values\n * @type (formName?: string) =\u003e ReturnType\u003ctypeof useFormInstance\u003e\n */\nconst formInst = useFormInstance();\n\n/**\n * Hook - return form (from context) store values or from provided form.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * @type (store: string, formName?: string) =\u003e IFormErrors\n */\nconst formErrors = useFormStore('$errors');\n\n/**\n * Hook - return form (from context) stores values array or from\n * provided form. Form name is needed when hook is used outside of the\n * form context or refers to another form.\n * @type (store: string[], formName?: string) =\u003e IFormErrors\n */\nconst [errors, values] = useFormStores(['$errors', '$values']);\n\n/**\n * Hook - return form (from context) values or from provided form.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * @type (formName?: string) =\u003e IFormValues\n */\nconst formValues = useFormValues();\n\n/**\n * Hook - return form (from context) methods or from provided form.\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * @type (formName?: string) =\u003e ReturnType\u003ctypeof useFormMethods\u003e\n */\nconst formMethods = useFormMethods();\n\n\n/**\n * Hook - return field data and methods combined\n * Form name is needed when hook is used outside of the form context\n * or refers to another form.\n * @type (name: string, formName?: string) =\u003e {\n *   value: any,\n *   active: boolean,\n *   dirty: boolean,\n *   error: string | null,\n *   errors: string[] | null,\n *   reset: () =\u003e void,\n *   validate: () =\u003e void,\n *   setActive: (value: boolean) =\u003e void,\n *   setValue: (value: any) =\u003e void,\n *   change: (value: any) =\u003e void,\n *   setConfig: (cfg: IFieldConfig) =\u003e void\n * }\n */\nconst field = useField('user.name');\n// Returns: { value, active, dirty, error, errors, reset, validate, setActive, setValue, change, setConfig }\n\n/**\n * Hook - return field value by name\n * Form name is needed when hook is used outside of form context\n * or refers to another form.\n * @type (name: string, formName?: string) =\u003e ReturnType\u003ctypeof useFieldData\u003e\n */\nconst fieldData = useFieldData('field-one');\n\n/**\n * Hook - return field store value\n * Form name is needed when hook is used outside of form context\n * or refers to another form.\n * @type (data: {\n *   store: string;\n *   name: string;\n *   formName?: string;\n *   defaultValue?: any;\n * }) =\u003e ReturnType\u003ctypeof useFieldStore\u003e\n */\nconst fieldActive = useFieldStore({\n  store: '$active',\n  name: 'user.name',\n  formName: 'login',\n  defaultValue: '',\n});\n\n/**\n * Hook - return field methods only\n * Form name is needed when hook is used outside of form context\n * or refers to another form.\n * @type (name: string, formName?: string) =\u003e ReturnType\u003ctypeof useFieldMethods\u003e\n */\nconst fieldMethods = useFieldMethods('user.name');\n// Returns: { reset, validate, setActive, setValue, change, setConfig }\n\n/**\n * Hook - return store value\n * @type (\n *   store: Store,\n *   prop: string,\n *   defaultValue?: any,\n * ) =\u003e ReturnType\u003ctypeof useStoreProp\u003e\n */\nconst storePropValue = useStoreProp(form.$values, 'user.name', '');\n```\n\n# Utils\n```ts\nimport {\n  // effector forms domain, usefull for logging / debugging\n  domain,\n  truthyFy,\n  shapeFy,\n  truthyFyStore,\n  shapeFyStore,\n  flattenObjectKeys,\n} from 'efx-forms/utils';\n\n/**\n * Return only truthy values from object\n * @type (values: IFormValues) =\u003e IFormValues\n */\nconst truthyValues = truthyFy(values);\n\n/**\n * Return flat to shaped values\n * @type (values: IFormValues) =\u003e {}\n * @example\n * { 'user.name': 'John' } =\u003e { user: { name: 'John } }\n */\nconst shapedValues = shapeFy(values);\n\n/**\n * Return effector store with truthy values\n * @type ($values: Store): Store =\u003e $truthyValues\n */\nconst $truthyStore = truthyFyStore($values);\n\n/**\n * Return effector store with shaped values\n * @type ($values: Store): Store =\u003e $shapedValues\n */\nconst $shapedStore = shapeFyStore($values);\n\n/**\n * Return flatten one level object keys/values\n * helper for the nested initial values\n * @type (values: Record\u003cstring, any\u003e): Record\u003cstring, any\u003e =\u003e ({})\n */\nconst initialValues = flattenObjectKeys(values);\n```\n\n# Validators\nCheck validators.d.ts file to see all built-in validators and their arguments\n```ts\nimport { required, email } from 'efx-forms/validators';\n\nconst formValidations = {\n  'user.name': [required()],\n  'user.email': [\n    required({ msg: 'Email is required' }), // custom message\n    email(),\n  ],\n}\n```\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarianstlex%2Fefx-forms","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdarianstlex%2Fefx-forms","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdarianstlex%2Fefx-forms/lists"}