{"id":13451920,"url":"https://github.com/harrysolovay/sowing-machine","last_synced_at":"2025-10-02T13:31:16.501Z","repository":{"id":57188915,"uuid":"175327198","full_name":"harrysolovay/sowing-machine","owner":"harrysolovay","description":"🌱A React UI toolchain \u0026 JSX alternative","archived":true,"fork":false,"pushed_at":"2020-06-08T06:06:09.000Z","size":874,"stargazers_count":64,"open_issues_count":1,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-19T18:06:58.503Z","etag":null,"topics":["babel","babel-macro","babel-plugin","eslint","eslint-config","eslint-plugin","jsx","markup","react"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/harrysolovay.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-13T01:56:10.000Z","updated_at":"2023-01-28T13:52:00.000Z","dependencies_parsed_at":"2022-08-28T11:11:45.364Z","dependency_job_id":null,"html_url":"https://github.com/harrysolovay/sowing-machine","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harrysolovay%2Fsowing-machine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harrysolovay%2Fsowing-machine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harrysolovay%2Fsowing-machine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/harrysolovay%2Fsowing-machine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/harrysolovay","download_url":"https://codeload.github.com/harrysolovay/sowing-machine/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235000753,"owners_count":18920237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":["babel","babel-macro","babel-plugin","eslint","eslint-config","eslint-plugin","jsx","markup","react"],"created_at":"2024-07-31T07:01:06.956Z","updated_at":"2025-10-02T13:31:16.120Z","avatar_url":"https://github.com/harrysolovay.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003ch1 align='center'\u003eSowing Machine\u003c/h1\u003e\n\n\u003cp align='center'\u003e\n\n\u003c!-- LICENSE --\u003e\n\u003ca href='https://opensource.org/licenses/MIT'\u003e\n  \u003cimg src='https://img.shields.io/packagist/l/doctrine/orm.svg' /\u003e\n\u003c/a\u003e\n\n\u003c!-- PRs --\u003e\n\u003ca href='http://makeapullrequest.com'\u003e\n  \u003cimg src='https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square' /\u003e\n\u003c/a\u003e\n\n\u003ca href='https://travis-ci.org/harrysolovay/sowing-machine'\u003e\n\t\u003cimg src='https://travis-ci.org/harrysolovay/sowing-machine.svg?branch=master' /\u003e\n\u003c/a\u003e\n\n\u003c/p\u003e\n\n## Guide\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Minimal Example](#minimal-example)\n- [Packages](#packages)\n- [Background](#background)\n- [Installation \u0026 Setup](#installation--setup)\n- [Describing UI](#describing-ui)\n- [Creating vs. Cloning](#creating-vs-cloning)\n- [Life Cycle](#life-cycle)\n- [Future Direction](#future-direction)\n- [Roadmap](#roadmap)\n- [Contributing](#contributing)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n## Minimal Example\n\n```js\nimport sow from 'sowing-machine'\nimport {useState} from 'react'\n\n// wrap the component with 'sow'\nconst Counter = sow(() =\u003e {\n  const [count, setCount] = useState(0)\n\n  const decrement = () =\u003e setCount(count - 1)\n  const increment = () =\u003e setCount(count + 1)\n\n  // express your UI and its embedded logic\n  return div({className: 'wrapper'})(\n    h1('current count: ', count),\n    div({className: 'controls'})(\n      button({onClick: decrement})`decrement`,\n      button({onClick: increment})`increment`,\n    ),\n  )\n})\n\n// use the component\nconst counterInstance = sow(Counter())\n```\n\n## Packages\n\n- [babel-plugin-sowing-machine](https://github.com/harrysolovay/sowing-machine/tree/master/packages/babel-plugin): compiles sowing machine code into a format that can be understood at runtime\n- [sowing-machine](https://github.com/harrysolovay/sowing-machine/tree/master/packages/runtime): the runtime library which interprets compiled `sow` calls\n- [eslint-config-sowing-machine](https://github.com/harrysolovay/sowing-machine/tree/master/packages/eslint-config): disables `no-undef` and enables a the `sowing-machine/no-undef` rule, which marks html tag identifiers as defined within sow calls\n- [sowing-machine.macro](https://github.com/harrysolovay/sowing-machine/tree/master/packages/babel-macro): makes use of the [babel-plugin-macros](https://github.com/kentcdodds/babel-plugin-macros) API to provide a more seamless integration with tools such as [create-react-app](https://github.com/facebook/create-react-app), [NextJS](https://nextjs.org/) and [GatsbyJS](https://www.gatsbyjs.org/)\n\n## Background\n\nHow do we feel about the coupling of UI logic \u0026 markup in React? Well, JSX does the trick... but it was born out of familiarity as a guiding principal (to ease the transition from HTML). This has been helpful to many user; after all, describing UI based off state is a new pattern, and JSX lowered the barrier to entry. But JSX also created some problems: beyond its redundancy, JSX suggests HTML-like behavior under the hood. Prop-passing resembles HTML attributes, instead of what it really is (passing an object to a function). Along with the misleading DX comes an extra parser stage for transpilation (in addition to the transform).\n\nBut what it really comes down to is this: **if you could have your pick of conventions, would you ever do the following?**\n\n```jsx\n// defining a function\nfunction add({a, b}) {\n  return a + b\n}\n\n// calling the function\nconst result = \u003cadd a={1} b={2} /\u003e\n```\n\nProbably not. Yet, this is what we do for every single piece of our UI in React.\n\nWe could get around using JSX by using `React.createElement` directly. [The bootstrapped create-react-app App component](https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/src/App.js)––for example––would look like this:\n\n```js\nimport {createElement as e} from 'react'\n\nconst App = e(\n  'div',\n  {className: 'App'},\n  e(\n    'header',\n    {className: 'App-header'},\n    e('img', {src: logo, className: 'App-logo', alt: 'logo'}),\n    e(\n      'p',\n      null,\n      'Edit ',\n      e('code', null, 'src/App.js'),\n      ' and save to reload.',\n    ),\n    e(\n      'a',\n      {\n        className: 'App-link',\n        href: 'https://reactjs.org',\n        target: '_blank',\n        rel: 'noopener noreferrer',\n      },\n      'Learn React',\n    ),\n  ),\n)\n```\n\nAlthough this lets us avoid JSX, it isn't very readable. `React.createElement` (`e`) is called 6 times. Depending on your formatter (prettier, for example), this could wrap in really unattractive ways. The current highlighting makes it difficult to distinguish grammar. JSX is––without a doubt––easier to work with.\n\nBut not as easy as Sowing Machine––a balance between preexisting options. It improves upon the function-call DX and lets you write less code to express the same UI. It's simple-enough to learn and master in minutes.\n\nHere's the create-react-app example using Sowing Machine:\n\n```js\nimport sow from 'sowing-machine'\n\nconst App = sow(() =\u003e\n  div({class: 'App'})(\n    header({class: 'App-header'})(\n      img({src: logo, class: 'App-logo', alt: 'logo'}),\n      p('Edit ', code`src/App.js`, ' and save to reload'),\n      a({\n        class: 'App-link',\n        href: 'https://reactjs.org',\n        target: '_blank',\n        rel: 'noopener noreferrer',\n      })`Learn React`,\n    ),\n  ),\n)\n```\n\n## Installation \u0026 Setup\n\n#### Install the following dev dependencies:\n\n```sh\nyarn add -D babel-plugin-sowing-machine eslint-config-sowing-machine\n```\n\n\u003e Sowing Machine is also available in Babel Macro form (`sowing-machine.macro`)\n\n#### Install the runtime library as a dependency\n\n```sh\nyarn add sowing-machine\n```\n\n\u003e there's no need to do this if you're using `sowing-machine.macro`\n\n#### Add the plugin to your Babel config\n\n`.babelrc`\n\n```diff\n{\n  \"plugins\": [\n+   \"sowing-machine\"\n  ]\n}\n```\n\n#### Add to your ESLint config as well\n\n`.eslintrc`\n\n```diff\n{\n  \"extends\": [\n+   \"sowing-machine\"\n  ]\n}\n```\n\nAnd voila! You're good to go!\n\n## Describing UI\n\nWrapping calls with `sow` ensures that they're transformed into valid runtime code.\n\n```diff\nimport sow from 'sowing-machine'\n\n- const HelloWorld = props =\u003e\n-   span(props)`Hello World!`\n\n+ const HelloWorld = sow(props =\u003e\n+   span(props)`Hello World!`\n+ )\n\n- const instance = HelloWorld()\n+ const instance = sow(HelloWorld())\n```\n\nTo use a 3rd-party component with Sowing Machine, we need to tell the compiler that it is in fact a component (and not just a function):\n\n```js\nimport sow from 'sowing-machine'\nimport {DatePicker} from '~/components'\n\n// wrapping with `sow` marks the `DatePicker` as sowable\nconst SDatePicker = sow(DatePicker)\n\n// use SDatePicker\nconst datePickerInstance = sow(SDatePicker())\n```\n\n### Ways to write an element\n\n\u003e note: in use, we need to wrap any of the following with `sow` (sow(div()))\n\n#### bare\n\n```js\nhr()\n```\n\n#### with props\n\n```js\ndiv({some: 'prop'})\n```\n\n#### with children\n\n```js\ndiv(div({className: 'first-child'}), div({className: 'second-child'}))\n```\n\n#### with text\n\n```js\nh1`This is cool!`\n```\n\n#### with props and children\n\n```js\nform({className: 'form'})(\n  input({type: 'email', name: 'email'}),\n  input({type: 'submit', value: 'Submit'}),\n)\n```\n\n#### with props \u0026 text\n\n```js\nspan({className: 'warning')`beware of dog`\n```\n\n## Creating vs. Cloning\n\nLet's say you have a component instance that you'd like to use as the basis for a new component. There's no way of expressing this with JSX. You'd need to do the following:\n\n```jsx\nimport React from 'react'\n\nconst instance = \u003cdiv\u003eHello\u003c/div\u003e\n\nconst usingTheInstance = (\n  \u003cdiv\u003e{React.cloneElement(instance, {}, 'Hello JSX')}\u003c/div\u003e\n)\n```\n\nWith Sowing Machine, the difference between using components and component instances is trivial:\n\n```js\nimport sow from 'sowing-machine'\n\nconst instance = sow(div`Hello`)\n\nconst usingTheInstance = sow(instance`Hello Sowing Machine`)\n```\n\n## Life Cycle\n\n### Compilation\n\nDuring compilation, Sowing Machine code is broken down into a format that can be understood at runtime. There's no way––without the compilation step––to simultaneously support the following ways of describing your UI:\n\n```js\n// these cannot co-exist\ndiv(props)\ndiv(children)\ndiv(props)(children)\ndiv`child text`\ndiv(props)`child text`\n```\n\nSowing Machine takes care of some additional complexity: differentiating between components, functions, and implicitly embedded logic.\n\nWhen working **with JSX, you need to mark your logic as off-limits to the compiler**:\n\n```jsx\nimport React from 'react'\n\nconst List = ({list}) =\u003e (\n  \u003cdiv\u003e\n    {list.map(e =\u003e (\n      \u003cdiv\u003e{e}\u003c/div\u003e\n    ))}\n  \u003c/div\u003e\n)\n```\n\n**...this isn't the case with Sowing Machine:**.\n\n```js\nimport sow from 'sowing-machine'\n\n// no need to manually distinguish between logic \u0026 markup\nconst List = sow(({list}) =\u003e div(list.map(e =\u003e div(e))))\n```\n\nThe compiler extracts key information from each function or tag call within your `sow` calls, and places them in a lightweight, runtime-friendly wrapper.\n\nThis code:\n\n```js\nsow(div({some: 'prop'})(span`neat`, span`syntax`))\n```\n\nGets transformed into something like this:\n\n```js\n_c(\n  _s('div', [\n    [{some: 'prop'}],\n    [_s('span', _x, [['neat']]), _s('span', _x, [['syntax']])],\n  ]),\n)\n```\n\n### Runtime\n\nWhen executing the code above, the runtime library will analyze the arguments of each `_s`.\n\nThe runtime goes through some of the following questions about each call: is the callee a React component? If so, are props applied? How about children? Both? Is the source a Tagged Template Expression? If we're dealing with a React component, we need to recombine the quasis. Otherwise, we need to pass quasis and expressions into the function according to [the Tagged Template Literal specification](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals). That way, libraries such as [`common-tags`](https://github.com/declandewet/common-tags) will still work.\n\n## Future Direction\n\nCompiling into a form that requires a runtime helper is heavily debatable (even though we do this pretty much everywhere, such as with ES6 classes). Without the runtime, it's impossible to support current `sowing-machine` syntax. However, there are other reasons that justify the (very slight) overhead. They mainly have to do with future direction for this toolchain:\n\n- allowing a 3rd-party to orchestrate component instanciation and function calls opens the door to some powerful optimizations. React users frequently fall into unintentional re-renders and re-calculations. A key goal for the runtime is to safeguard against these pitfalls.\n- styling! If you've ever used a CSS-in-JS library, chances are that you create a container component, assign it nested styles, and wrap your component root. Another key goal for the runtime is to support this pattern:\n\n```js\nimport sow from 'sowing-machine'\n\nconst BlueBoxWithHello = sow(() =\u003e div`Hello`)`\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  text-align: center;\n  width: 100px;\n  height: 100px;\n  background-color: blue;\n  color: #fff;\n`\n```\n\nThis API will be fairly minimal. It will include nested-css support and vendor-prefixing at runtime. That about covers it.\n\n## Roadmap\n\n|    Status     | Goal                                           | package               |\n| :-----------: | :--------------------------------------------- | --------------------- |\n| `in progress` | TypeScript definitions                         | runtime               |\n| `in progress` | Container memoization \u0026 invalidation           | runtime               |\n| `in progress` | Applying nested styles tagged onto `sow` calls | runtime               |\n| `in progress` | Better errors                                  | babel-plugin, runtime |\n|  `planning`   | More ESLint rules                              | eslint-plugin         |\n\n## Contributing\n\nIf you have a feature idea or want to contribute, please go ahead and file an issue 💡\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharrysolovay%2Fsowing-machine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fharrysolovay%2Fsowing-machine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fharrysolovay%2Fsowing-machine/lists"}