{"id":23770966,"url":"https://github.com/bubblydoo/angular-react","last_synced_at":"2025-04-07T13:05:57.485Z","repository":{"id":65392401,"uuid":"495778094","full_name":"bubblydoo/angular-react","owner":"bubblydoo","description":"Use React in Angular and Angular in React, easily","archived":false,"fork":false,"pushed_at":"2024-11-05T21:30:18.000Z","size":2133,"stargazers_count":35,"open_issues_count":0,"forks_count":9,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-03-31T12:05:13.417Z","etag":null,"topics":["angular","microfrontend","react"],"latest_commit_sha":null,"homepage":"https://dev.to/bubblydoo/transitioning-from-angular-to-react-without-starting-from-scratch-j66","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/bubblydoo.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}},"created_at":"2022-05-24T10:39:43.000Z","updated_at":"2025-02-17T00:50:14.000Z","dependencies_parsed_at":"2025-01-08T04:37:11.651Z","dependency_job_id":null,"html_url":"https://github.com/bubblydoo/angular-react","commit_stats":{"total_commits":57,"total_committers":1,"mean_commits":57.0,"dds":0.0,"last_synced_commit":"325b1e76801ef396ad908c83b8abe1ea1de5fc8c"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubblydoo%2Fangular-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubblydoo%2Fangular-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubblydoo%2Fangular-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bubblydoo%2Fangular-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bubblydoo","download_url":"https://codeload.github.com/bubblydoo/angular-react/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247657276,"owners_count":20974344,"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":["angular","microfrontend","react"],"created_at":"2025-01-01T03:18:42.109Z","updated_at":"2025-04-07T13:05:57.451Z","avatar_url":"https://github.com/bubblydoo.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://www.npmjs.com/package/@bubblydoo/angular-react\" alt=\"NPM\"\u003e\n  \u003cimg src=\"https://img.shields.io/npm/v/@bubblydoo/angular-react\" alt=\"NPM\"/\u003e\n\u003c/a\u003e\n\u003ca href=\"https://angular-react-bubblydoo.vercel.app/\" alt=\"Storybook\"\u003e\n  \u003cimg src=\"https://img.shields.io/badge/Storybook-e1618c.svg?colorA=26077C\u0026logo=data:image/png;base64,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\" alt=\"Storybook\"/\u003e\n\u003c/a\u003e\n\n# React in Angular and Angular in React\n\nThis is a small Angular library that lets you use React components inside Angular projects.\n\n```html\n\u003creact-wrapper [component]=\"Button\" [props]=\"{ children: 'Hello world!' }\"\u003e\n```\n\n```tsx\nfunction ReactComponent({ text }) {\n  return \u003cAngularWrapper component={TextComponent} inputs={{ text }}\u003e\n}\n```\n\n## Installation\n\n```bash\nnpm i @bubblydoo/angular-react\n```\n\n```ts\nimport { AngularReactModule } from '@bubblydoo/angular-react'\n\n@NgModule({\n  ...,\n  imports: [\n    ...,\n    AngularReactModule\n  ]\n})\n```\n\n## Features\n\n### `ReactWrapperComponent`\n\nUse this component when you want to use React in Angular.\n\nIt takes two inputs:\n- `component`: A React component\n- `props?`: The props you want to pass to the React component\n\nThe React component will be first rendered on `ngAfterViewInit` and rerendered on every `ngOnChanges` call.\n\n```ts\nimport Button from './button'\n\n@Component({\n  template: `\u003creact-wrapper [component]=\"Button\" [props]=\"{ children: 'Hello world!' }\"\u003e`\n})\nclass AppComponent {\n  Button = Button\n}\n```\n\n### `AngularWrapper`\n\nUse this component when you want to use Angular in React.\n\nIt takes a few inputs:\n- `component`: An Angular component\n- `inputs?`: The inputs you want to pass to the Angular component, in an object\n- `outputs?`: The outputs you want to pass to the Angular component, in an object\n- `events?`: The events from the Angular component to listen to, using `addEventListener`. Event handlers are wrapped in `NgZone.run`\n- `ref?`: The ref to the rendered DOM element (uses `React.forwardRef`)\n\n```tsx\nimport { TextComponent } from './text/text.component'\n\nfunction Text(props) {\n  return (\n    \u003cAngularWrapper\n      component={TextComponent}\n      inputs={{ text: props.text }}\n      events={{ click: () =\u003e console.log('clicked') }}/\u003e\n  )\n}\n```\n\n### `useInjected`\n\nThe Angular Injector is provided on each React component by default using React Context. You can use Angular services and other injectables with it:\n\n```tsx\nimport { useInjected } from '@bubblydoo/angular-react'\n\nconst authService = useInjected(AuthService)\n```\n\n### `useObservable`\n\nBecause consuming observables is so common, we added a helper hook for it:\n\n```tsx\nimport { useObservable, useInjected } from '@bubblydoo/angular-react'\n\nfunction LoginStatus() {\n  const authService = useInjected(AuthService)\n\n  const [value, error, completed] = useObservable(authService.isAuthenticated$)\n\n  if (error) return \u003c\u003eSomething went wrong!\u003c\u003e\n\n  return \u003c\u003e{value ? \"Logged in!\" : \"Not logged in\"}\u003c/\u003e\n}\n```\n\n### Global React Context\n\nIf you want to have a global React Context, you can register it as follows:\n\n```ts\n// app.component.ts\n\nconstructor(angularReact: AngularReactService) {\n  const client = new ApolloClient()\n  // equivalent to ({ children }) =\u003e \u003cApolloProvider client={client}\u003e{children}\u003c/ApolloProvider\u003e\n  angularReact.wrappers.push(({ children }) =\u003e React.createElement(ApolloProvider, { client, children }))\n}\n```\n\nIn this example, we use `ApolloProvider` to provide a client to each React element. We can then use `useQuery` in all React components.\n\nThis is only needed when your host app is an Angular app. If you're using Angular-in-React, the context will be bridged.\n\n### Refs\n\nYou can get a ref to the Angular component instance as follows:\n\n```tsx\nimport { ComponentRef } from '@angular/core'\n\nconst ref = useRef\u003cComponentRef\u003cany\u003e\u003e()\n\n\u003cAngularWrapper ref={ref} /\u003e\n```\n\nTo get the component instance, use `ref.instance`. To get a reference to the Angular component's HTML element, use `ref.location.nativeElement`.\n\nTo forward a ref to a React component, you can simply use the props:\n\n```tsx\nconst Message = forwardRef((props, ref) =\u003e {\n  return \u003cdiv ref={ref}\u003e{props.message}\u003c/div\u003e\n})\n\n@Component({\n  template: `\u003creact-wrapper [component]=\"Message\" [props]=\"{ ref, message }\"\u003e`,\n})\nexport class MessageComponent {\n  Message = Message\n\n  message = 'hi!'\n\n  ref(div: HTMLElement) {\n    div.innerHTML = 'hi from the callback ref!'\n  }\n}\n```\n\n### Reading React contexts in Angular\n\n```tsx\n@Component({\n  selector: \"inner\",\n  template: `number: {{ number$ | async }}`,\n  changeDetection: ChangeDetectionStrategy.OnPush,\n})\nclass InnerComponent {\n  number$ = this.contexts.read(NumberContext)\n\n  constructor(@Inject(InjectableReactContextToken) public contexts: InjectableReactContext) {}\n}\n\nfunction App() {\n  const [number, setNumber] = useState(42)\n  return (\n    \u003cNumberContext.Provider value={number}\u003e\n      \u003cbutton onClick={() =\u003e setNumber(number + 1)}\u003eincrement\u003c/button\u003e\n      \u003cAngularWrapper component={InnerComponent} /\u003e\n    \u003c/NumberContext.Provider\u003e\n  )\n}\n```\n\n### Using templates\n\n#### `useToAngularTemplateRef`: to convert a React component into a `TemplateRef`\n\n```tsx\nimport { useToAngularTemplateRef } from \"@bubblydoo/angular-react\"\n\n@Component({\n  selector: 'message',\n  template: `\n    \u003cdiv\u003e\n      \u003cng-container\n        [ngTemplateOutlet]=\"tmpl\"\n        [ngTemplateOutletContext]=\"{ message }\"\n        [ngTemplateOutletInjector]=\"injector\"\n      \u003e\u003c/ng-container\u003e\n    \u003c/div\u003e\n  `,\n})\nclass MessageComponent {\n  @Input() tmpl: TemplateRef\u003c{ message: string }\u003e\n  @Input() message: string\n\n  constructor(public injector: Injector) {}\n}\n\nfunction Text(props: { message: string }) {\n  return \u003c\u003e{props.message}\u003c/\u003e\n}\n\nfunction Message(props: { message: string }) {\n  const tmpl = useToAngularTemplateRef(Text)\n\n  const inputs = useMemo(() =\u003e ({\n    message: props.message,\n    tmpl,\n  }), [props.message, tmpl])\n\n  return \u003cAngularWrapper component={MessageComponent} inputs={inputs} /\u003e\n}\n```\n\nNote: `useToAngularTemplateRef` is meant for usage with `[ngTemplateOutletInjector]=\"injector\"`. If you can't use that, use `useToAngularTemplateRefBoundToContextAndPortals` instead.\n\n#### `useFromAngularTemplateRef`: to convert a `TemplateRef` into a React component\n\n```tsx\nfunction Message(props: {\n  message: string\n  tmpl: TemplateRef\u003c{ message: string }\u003e\n}) {\n  const Template = useFromAngularTemplateRef(props.tmpl)\n\n  return \u003cTemplate message={props.message.toUpperCase()} /\u003e\n}\n\n@Component({\n  selector: \"outer\",\n  template: `\n    \u003cng-template #tmpl let-message=\"message\"\u003e{{ message }}\u003c/ng-template\u003e\n    \u003cdiv\u003e\n      \u003creact-wrapper\n        [component]=\"Message\"\n        [props]=\"{ tmpl, message }\"\n      \u003e\u003c/react-wrapper\u003e\n    \u003c/div\u003e\n  `,\n})\nclass MessageComponent {\n  Message = Message\n\n  @Input() message!: string\n}\n```\n\n## Developing\n\nYou can test the functionality of the components inside a local Storybook:\n\n```bash\nyarn storybook\n```\n\nIf you want to use your local build in an Angular project, you'll need to build it:\n\n```bash\nyarn build\n```\n\nThen, use `yarn link`:\n\n```bash\ncd dist/angular-react\nyarn link # this will link @bubblydoo/angular-react to dist/angular-react\n# or `npm link`\n```\n\nIn your Angular project:\n\n```bash\nyarn link @bubblydoo/angular-react\n# or `npm link @bubblydoo/angular-react`\n```\n\n`node_modules/@bubblydoo/angular-react` will then be symlinked to `dist/angular-react`.\n\nYou might want to use resolutions or overrides if you run into NG0203 errors.\n\n```json\n\"resolutions\": {\n  \"@bubblydoo/angular-react\": \"file:../angular-react/dist/angular-react\"\n}\n```\n\n## Usage notes\n\n### `this` is undefined when passing an Angular component method as a React prop\n\nAngular component methods are always called with the component instance as `this`. When you pass an Angular method as a prop to a React component, `this` will be `undefined`.\n\n```ts\n@Component({\n  template: `\u003creact-wrapper [component]=\"Button\" [props]=\"{ onClick }\"\u003e`\n})\nclass AppComponent {\n  Button = Button\n\n  onClick() {\n    console.log(this) // undefined\n  }\n}\n```\n\nYou can fix it as follows:\n\n```ts\n@Component({\n  template: `\u003creact-wrapper [component]=\"Button\" [props]=\"{ onClick }\"\u003e`\n})\nclass AppComponent {\n  Button = Button\n\n  onClick = () =\u003e {\n    console.log(this) // AppComponent instance\n  }\n}\n```\n\n## Further reading\n\nSee this blog post for the motivation and more details: [Transitioning from Angular to React, without starting from scratch](https://dev.to/bubblydoo/transitioning-from-angular-to-react-without-starting-from-scratch-j66)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbubblydoo%2Fangular-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbubblydoo%2Fangular-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbubblydoo%2Fangular-react/lists"}