{"id":28413297,"url":"https://github.com/vikbert/symfony-react-todo","last_synced_at":"2025-06-24T20:30:58.690Z","repository":{"id":39513305,"uuid":"191213834","full_name":"vikbert/symfony-react-todo","owner":"vikbert","description":"React Todo integration with Symfony 4 Project with server-rendering and hot-reloading","archived":false,"fork":false,"pushed_at":"2023-01-03T23:51:46.000Z","size":6618,"stargazers_count":1,"open_issues_count":28,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-06-03T15:26:22.554Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","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/vikbert.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-06-10T17:25:18.000Z","updated_at":"2023-03-09T00:58:22.000Z","dependencies_parsed_at":"2023-02-01T14:15:30.754Z","dependency_job_id":null,"html_url":"https://github.com/vikbert/symfony-react-todo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vikbert/symfony-react-todo","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vikbert%2Fsymfony-react-todo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vikbert%2Fsymfony-react-todo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vikbert%2Fsymfony-react-todo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vikbert%2Fsymfony-react-todo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vikbert","download_url":"https://codeload.github.com/vikbert/symfony-react-todo/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vikbert%2Fsymfony-react-todo/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":261751444,"owners_count":23204426,"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":[],"created_at":"2025-06-03T04:33:50.516Z","updated_at":"2025-06-24T20:30:58.678Z","avatar_url":"https://github.com/vikbert.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"Symfony React Todo\n=====================\n\nIt has three main areas of interest:\n\n* The server-side code under `src/` and `app/config` configuration.\n* The JavaScript and CSS (SCSS) code under `assets/`.\n* The Webpack Encore configuration for client and server-side rendering at `webpack.config.js` and `webpack.config.serverside.js`.\n\n## Acknowledgments\n* [ReactBundle](https://github.com/limenius/ReactBundle)\n* [PhpExecJs](https://github.com/nacmartin/phpexecjs)\n* [React on Rails](https://github.com/shakacode/react_on_rails)\n\n\n\nHow to run it\n=============\n\n#### Step 1\nFirst of all, check the source code and install PHP packages:\n\n    git clone https://github.com/vikbert/symfony-react-todo.git\n    cd symfony-react-sandbox\n    composer install\n\n#### Step 2\nAnd then, run a live server with Webpack hot-reloading of assets. As Prerequisites, the following node packages should be installed:\n\n    npm install -g webpack webpack-dev-server\n\n#### Step 3 \nBuilding the server-side react Webpack bundle for server side rendering:\n\n    npm run webpack-serverside\n\n#### Step 4\nthen start the hot-reloading webpack server for the client assets: `assets/js` or `assets/css`\n\n    npm run webpack-dev\n\n#### Step 5\nat last, run the Symfony server to serve the symfony web:\n\n    bin/console server:start\n\nAfter this, visit [http://127.0.0.1:8000](http://127.0.0.1:8000).\n\nWhy Webpack?\n===========\n\nWebpack is used to generate two separate JavaScript bundles (that share some code). One is meant for its inclusion as context for the server-side rendering. The second will contain your client-side frontend code. Given this, we can write Twig code to render React components like, for instance:\n\n    {{ react_component('RecipesApp', {'props': props}) }}\n\nAnd it will be rendered both client and server-side.\n\nWe have provided what we think are sensible defaults for both bundles, and also for the package.json dependencies, like Twitter Bootstrap and such. Feel free to adapt them to your needs.\n\nPlease note that if you are copying `webpack.config.js` or `webpack.config.server.js` to your project it is very likely that you will need also `.babelrc` to have Babel presets (used to transform React JSX and modern JavaScript to plain old JavaScript)\n\nWhy Server-Side rendering?\n==========================\n\nIf you enable server-side rendering along with client-side rendering of components (this is the default) your React components will be rendered directly as HTML by Twig and then, when the client-side code is run, React will identify the already rendered HTML and it won't render it again until is needed. Instead, it will silently take control over it and re-render it only when it is needed.\n\nThis can be vital for some applications for SEO purposes or where a bot has to scrape the content (Facebook bot scraping `og:` tags for instance), but also is great for quick page-loads and to provide the content to users with JavaScript disabled (if there is any left, or if you have plans to build progressive web applications).\n\nYou can configure ReactBundle to have server-side, client-side or both. See the bundle documentation for more information.\n\nHow it works\n============\n\nWhen you render a React Component in a Twig template with `{{ react_component('RecipesApp', {'props': props}) }}` with client and server-side rendering enabled (this is the default setting), ReactBundle will render a `\u003cdiv\u003e` that will serve as container of the component.\n\nInside of it, the bundle will place all the HTML code that results of evaluating your component. It will do so by calling `PhpExecJs`, using your server-bundle, generated by Webpack as context, and retrieving the outcome.\n\nWhen your client-side JavaScript runs, React will find this `\u003cdiv\u003e` tag and will recognize it as the result of rendering a component. It won't render it again (unless the evaluation of your client-side code differs), but it will take control of it, and, depending on the actions performed by the user, it will re-render the component dynamically.\n\nWalkthrough\n===========\n\nWe have set-up a simple application. A recipes App with master-detail views. In the actions of the controller under `src/AppBundle/Controller/RecipeController.php` you will find two types of actions.\n\n### Actions that render Twig templates.\n\nThese actions retrieve the recipes that will be shown in the page and pass them as a JSON string to template.\n\nIn the Twig templates under `/app/Resouces/views/recipe/` you will find templates with code like tthis one:\n\n    {{ react_component('RecipesApp', {'props': props}) }}\n\nThis Twig function, provided by [ReactBundle](https://github.com/limenius/ReactBundle), will render the React component `RecipesApp` in server and client modes.\n\n### Actions that render JSON responses.\n\nThese actions act as an API and will be used by the client-side React code to retrieve data as needed when navigating to other pages without reloading the pages.\n\nTo simplify things we don't use FOSRestBundle here, but feel free to use it to build your API.\n\n### Globally expose your React components\n\nIn order to make your React components accessible to ReactBundle, you need to register them. We are using for this purpose the npm package of the React On Rails, (that can be used outside the Ruby world).\n\nTake a look at the `assets/js/recipes/startup/registration.js` file:\n\nServer side:\n\n    import ReactOnRails from 'react-on-rails'\n    import RecipesApp from './RecipesApp'\n\n    ReactOnRails.register({ RecipesApp })\n\n#### JavaScript code organization for isomorphic apps\n\nNote that in most cases you will be sharing almost all of your code between your client-side component and its server-side homologous, but while your client-code comes with no surprises, in the server side you will probably have to play a bit with `react-router` in order to let it know the location and set up the routing history. This is a common issue in isomorphic applications. You can find examples on how to do this all along the Internet, but also in the file `assets/js/recipes/RecipesApp.js`.\n\nNote that React on Rails passes a second `context` parameter to the root container that includes the property `serverSide`:\n\n\n    export default (initialProps, context) =\u003e {\n\n        if (context.serverSide) {\n            /...\n        } else {\n            /...\n        }\n\nRedux example\n=============\n\nThere is a working example using Redux at `assets/js/recipes-redux/`, and available at the URI `/redux/`.\n\nNote that the presentational components of both versions are shared, as they don't know about Redux.\n\nLiform example\n=============\n\nThere is also an example of working with forms using [LiformBundle](https://github.com/Limenius/LiformBundle), so Symfony forms are serialized into [json-schema](http://json-schema.org/), and then generated automatically in React, and can be validated against the generated schema. The idea is similar as what `$form-\u003ecreateView()` does, but for APIs.\n\nThis example can be accessed at the URI `/admin/liform/`.\n\nUsage with JWT\n=============\n\nThis sandbox uses [LexikJWTAuthenticationBundle](https://github.com/lexik/LexikJWTAuthenticationBundle) to handle authentication in the admin area.\n\nIf you don't plan to use server side rendering in private areas, using JWT is straightforward. However, as this is a sandbox, so a place to try things, we have provided an example that works also with server side rendering. This involves setting the JWT token in a cookie after the login and extracting the token and validating it in the controller that loads the admin panel.\n\nThe relevant pieces of code involved are [here](https://github.com/Limenius/symfony-react-sandbox/blob/symfony4/assets/js/liform/actions/index.js) and [here](https://github.com/Limenius/symfony-react-sandbox/blob/symfony4/src/Controller/AdminController.php).\n\nNote that if you plan to copy and paste this sandbox and use it for something serious, you **should** regenerate the crypto keys following the documentation of LexikJWTAuthenticationBundle.\n\nServer side rendering modes\n===========================\n\nThis library supports two modes of using server-side rendering:\n\n* Using [PhpExecJs](https://github.com/nacmartin/phpexecjs) to auto-detect a JavaScript environment (call node.js via terminal command or use V8Js PHP) and run JavaScript code through it.\n\n* Using an external node.js server ([Example](https://github.com/Limenius/symfony-react-sandbox/blob/master/external-server.js). It will use a dummy server, that knows nothing about your logic to render React for you. Introduces more operational complexity (you have to keep the node server running, which is not a big deal anyways).\n\nCurrently, the best option is to use an external server in production, since having [V8js](https://github.com/phpv8/v8js) is rather hard to compile. However, if you can compile it or your distribution/OS has good packages, it is a very good option if you enable caching, as we will see in the next section.\n\n### Cache\n\nif in your config.prod.yaml or `config/packages/prod/limenius_react.yaml` you add the following configuration, and you have V8js installed, this bundle will be much faster:\n\n    limenius_react:\n        serverside_rendering:\n            cache:\n                enabled: true\n                # name of your app, it is the key of the cache where the snapshot will be stored.\n                key: \"recipes_app\"\n\nAfter the first page render, this will store a snapshot of the JS virtual machine V8js in the cache, so in subsequent visits, your whole JavaScript app doesn't need to be processed again, just the particular component that you want to render.\n\nWith the cache enabled, if you change code of your JS app, you will need to clear the cache.\n\nCredits\n=======\n\nThis project is heavily inspired by the great [React on Rails](https://github.com/shakacode/react_on_rails#), and also makes use of its JavaScript package.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvikbert%2Fsymfony-react-todo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvikbert%2Fsymfony-react-todo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvikbert%2Fsymfony-react-todo/lists"}