[{"data":1,"prerenderedAt":817},["ShallowReactive",2],{"msfe-doc:\u002Fen\u002Fguide\u002Freact":3},{"id":4,"title":5,"body":6,"description":804,"extension":805,"keywords":806,"locale":810,"meta":811,"navigation":88,"order":39,"path":812,"section":813,"seo":814,"stem":815,"__hash__":816},"docs\u002Fguide\u002Freact.en.md","React integration",{"type":7,"value":8,"toc":797},"minimark",[9,14,26,205,209,232,422,426,438,441,445,448,767,783,787,790,793],[10,11,13],"h2",{"id":12},"minimal-integration","Minimal integration",[15,16,17,21,22,25],"p",{},[18,19,20],"code",{},"MsfeEditorContent"," handles mounting and cleanup, and ",[18,23,24],{},"onReady"," exposes the current instance. Options provide initial configuration; use instance methods to update an editor that is already mounted.",[27,28,33],"pre",{"className":29,"code":30,"language":31,"meta":32,"style":32},"language-tsx shiki shiki-themes github-light github-light github-dark","import { MsfeEditorContent } from '@quirekit\u002Fmsfe-react'\nimport { StarterKit } from '@quirekit\u002Fmsfe-starter-kit'\nimport type { ReactEditorOptions } from '@quirekit\u002Fmsfe-react'\n\nconst options: ReactEditorOptions = {\n  markdown: '# Hello, Markdown',\n  mode: 'live',\n  extensions: [StarterKit],\n}\n\n\u002F** @returns The editor component in the current application. *\u002F\nexport function EditorExample() {\n  return \u003CMsfeEditorContent options={options} \u002F>\n}\n","tsx","",[18,34,35,55,68,83,90,113,125,136,142,148,153,166,181,200],{"__ignoreMap":32},[36,37,40,44,48,51],"span",{"class":38,"line":39},"line",1,[36,41,43],{"class":42},"sDD9d","import",[36,45,47],{"class":46},"sVPnS"," { MsfeEditorContent } ",[36,49,50],{"class":42},"from",[36,52,54],{"class":53},"s-HEn"," '@quirekit\u002Fmsfe-react'\n",[36,56,58,60,63,65],{"class":38,"line":57},2,[36,59,43],{"class":42},[36,61,62],{"class":46}," { StarterKit } ",[36,64,50],{"class":42},[36,66,67],{"class":53}," '@quirekit\u002Fmsfe-starter-kit'\n",[36,69,71,73,76,79,81],{"class":38,"line":70},3,[36,72,43],{"class":42},[36,74,75],{"class":42}," type",[36,77,78],{"class":46}," { ReactEditorOptions } ",[36,80,50],{"class":42},[36,82,54],{"class":53},[36,84,86],{"class":38,"line":85},4,[36,87,89],{"emptyLinePlaceholder":88},true,"\n",[36,91,93,96,100,103,107,110],{"class":38,"line":92},5,[36,94,95],{"class":42},"const",[36,97,99],{"class":98},"sUNQB"," options",[36,101,102],{"class":42},":",[36,104,106],{"class":105},"sgQzM"," ReactEditorOptions",[36,108,109],{"class":42}," =",[36,111,112],{"class":46}," {\n",[36,114,116,119,122],{"class":38,"line":115},6,[36,117,118],{"class":46},"  markdown: ",[36,120,121],{"class":53},"'# Hello, Markdown'",[36,123,124],{"class":46},",\n",[36,126,128,131,134],{"class":38,"line":127},7,[36,129,130],{"class":46},"  mode: ",[36,132,133],{"class":53},"'live'",[36,135,124],{"class":46},[36,137,139],{"class":38,"line":138},8,[36,140,141],{"class":46},"  extensions: [StarterKit],\n",[36,143,145],{"class":38,"line":144},9,[36,146,147],{"class":46},"}\n",[36,149,151],{"class":38,"line":150},10,[36,152,89],{"emptyLinePlaceholder":88},[36,154,156,160,163],{"class":38,"line":155},11,[36,157,159],{"class":158},"sqmzS","\u002F** ",[36,161,162],{"class":42},"@returns",[36,164,165],{"class":158}," The editor component in the current application. *\u002F\n",[36,167,169,172,175,178],{"class":38,"line":168},12,[36,170,171],{"class":42},"export",[36,173,174],{"class":42}," function",[36,176,177],{"class":105}," EditorExample",[36,179,180],{"class":46},"() {\n",[36,182,184,187,190,192,194,197],{"class":38,"line":183},13,[36,185,186],{"class":42},"  return",[36,188,189],{"class":46}," \u003C",[36,191,20],{"class":98},[36,193,99],{"class":105},[36,195,196],{"class":42},"=",[36,198,199],{"class":46},"{options} \u002F>\n",[36,201,203],{"class":38,"line":202},14,[36,204,147],{"class":46},[10,206,208],{"id":207},"custom-host-layout","Custom host layout",[15,210,211,212,215,216,219,220,223,224,227,228,231],{},"To place the mount element yourself, use ",[18,213,214],{},"useMsfeEditor(parentRef, optionsFactory)",". It returns the current session or ",[18,217,218],{},"null","; the session contains ",[18,221,222],{},"editor"," and ",[18,225,226],{},"host",". Keep ",[18,229,230],{},"ReactNodeViewHost"," within your existing provider tree.",[27,233,235],{"className":29,"code":234,"language":31,"meta":32,"style":32},"import { useRef } from 'react'\nimport { useMsfeEditor, ReactNodeViewHost } from '@quirekit\u002Fmsfe-react'\nimport { StarterKit } from '@quirekit\u002Fmsfe-starter-kit'\n\n\u002F** @returns An editor and component-view host that retain context. *\u002F\nexport function EditorWithHost() {\n  const parent = useRef\u003CHTMLDivElement>(null)\n  const session = useMsfeEditor(parent, () => ({\n    markdown: 'Start editing.',\n    extensions: [StarterKit],\n  }))\n\n  return \u003C>\n    \u003Cdiv ref={parent} \u002F>\n    {session && \u003CReactNodeViewHost host={session.host} \u002F>}\n  \u003C\u002F>\n}\n",[18,236,237,249,260,270,274,283,294,321,342,352,357,362,366,373,390,411,417],{"__ignoreMap":32},[36,238,239,241,244,246],{"class":38,"line":39},[36,240,43],{"class":42},[36,242,243],{"class":46}," { useRef } ",[36,245,50],{"class":42},[36,247,248],{"class":53}," 'react'\n",[36,250,251,253,256,258],{"class":38,"line":57},[36,252,43],{"class":42},[36,254,255],{"class":46}," { useMsfeEditor, ReactNodeViewHost } ",[36,257,50],{"class":42},[36,259,54],{"class":53},[36,261,262,264,266,268],{"class":38,"line":70},[36,263,43],{"class":42},[36,265,62],{"class":46},[36,267,50],{"class":42},[36,269,67],{"class":53},[36,271,272],{"class":38,"line":85},[36,273,89],{"emptyLinePlaceholder":88},[36,275,276,278,280],{"class":38,"line":92},[36,277,159],{"class":158},[36,279,162],{"class":42},[36,281,282],{"class":158}," An editor and component-view host that retain context. *\u002F\n",[36,284,285,287,289,292],{"class":38,"line":115},[36,286,171],{"class":42},[36,288,174],{"class":42},[36,290,291],{"class":105}," EditorWithHost",[36,293,180],{"class":46},[36,295,296,299,302,304,307,310,313,316,318],{"class":38,"line":127},[36,297,298],{"class":42},"  const",[36,300,301],{"class":98}," parent",[36,303,109],{"class":42},[36,305,306],{"class":105}," useRef",[36,308,309],{"class":46},"\u003C",[36,311,312],{"class":105},"HTMLDivElement",[36,314,315],{"class":46},">(",[36,317,218],{"class":98},[36,319,320],{"class":46},")\n",[36,322,323,325,328,330,333,336,339],{"class":38,"line":138},[36,324,298],{"class":42},[36,326,327],{"class":98}," session",[36,329,109],{"class":42},[36,331,332],{"class":105}," useMsfeEditor",[36,334,335],{"class":46},"(parent, () ",[36,337,338],{"class":42},"=>",[36,340,341],{"class":46}," ({\n",[36,343,344,347,350],{"class":38,"line":144},[36,345,346],{"class":46},"    markdown: ",[36,348,349],{"class":53},"'Start editing.'",[36,351,124],{"class":46},[36,353,354],{"class":38,"line":150},[36,355,356],{"class":46},"    extensions: [StarterKit],\n",[36,358,359],{"class":38,"line":155},[36,360,361],{"class":46},"  }))\n",[36,363,364],{"class":38,"line":168},[36,365,89],{"emptyLinePlaceholder":88},[36,367,368,370],{"class":38,"line":183},[36,369,186],{"class":42},[36,371,372],{"class":46}," \u003C>\n",[36,374,375,378,382,385,387],{"class":38,"line":202},[36,376,377],{"class":46},"    \u003C",[36,379,381],{"class":380},"su0ou","div",[36,383,384],{"class":105}," ref",[36,386,196],{"class":42},[36,388,389],{"class":46},"{parent} \u002F>\n",[36,391,393,396,399,401,403,406,408],{"class":38,"line":392},15,[36,394,395],{"class":46},"    {session ",[36,397,398],{"class":42},"&&",[36,400,189],{"class":46},[36,402,230],{"class":98},[36,404,405],{"class":105}," host",[36,407,196],{"class":42},[36,409,410],{"class":46},"{session.host} \u002F>}\n",[36,412,414],{"class":38,"line":413},16,[36,415,416],{"class":46},"  \u003C\u002F>\n",[36,418,420],{"class":38,"line":419},17,[36,421,147],{"class":46},[10,423,425],{"id":424},"context-and-strictmode","Context and StrictMode",[15,427,428,223,431,434,435,437],{},[18,429,430],{},"ReactNodeViewRenderer()",[18,432,433],{},"ReactComponentViewRenderer()"," use portals within the same host. Development StrictMode repeats the effect lifecycle, so ",[18,436,24],{}," may run again; do not treat it as a business action that runs exactly once.",[15,439,440],{},"QuireKit's public component library currently provides Vue components. The React adapter supports your application's React UI while keeping the field and transaction contracts consistent.",[10,442,444],{"id":443},"react-nodeview-shape","React NodeView shape",[15,446,447],{},"The component receives nodeView and reuses the framework-independent codec. Use the Portal Host to preserve Context instead of creating a separate React root per node.",[27,449,451],{"className":29,"code":450,"language":31,"meta":32,"style":32},"import type { MsfeNodeViewProps } from '@quirekit\u002Fmsfe-core'\nimport { MsfeEditorContent, ReactNodeViewRenderer } from '@quirekit\u002Fmsfe-react'\nimport { StarterKit } from '@quirekit\u002Fmsfe-starter-kit'\nimport { Status } from '.\u002Fstatus'\n\ninterface StatusAttributes { readonly value: 'todo' | 'done' }\nfunction StatusView({ nodeView }: { nodeView: MsfeNodeViewProps\u003CStatusAttributes> }) {\n  return \u003Cbutton disabled={nodeView.readOnly} onClick={() => nodeView.updateAttributes({\n    value: nodeView.attrs.value === 'done' ? 'todo' : 'done',\n  })}>{nodeView.attrs.value}\u003C\u002Fbutton>\n}\n\u002F\u002F Status is the framework-independent extension from the custom-node tutorial.\nconst StatusWithUI = Status.extend({\n  addNodeView() { return ReactNodeViewRenderer(StatusView) },\n})\n\n\u002F** @returns A mounted editor using the custom React node view. *\u002F\nexport function StatusEditor() {\n  return \u003CMsfeEditorContent options={{\n    markdown: '# Status\\n\\n[[todo]]', mode: 'live',\n    extensions: [StarterKit, StatusWithUI],\n  }} \u002F>\n}\n",[18,452,453,467,478,488,500,504,536,572,608,630,640,644,649,666,683,688,692,701,713,729,750,756,762],{"__ignoreMap":32},[36,454,455,457,459,462,464],{"class":38,"line":39},[36,456,43],{"class":42},[36,458,75],{"class":42},[36,460,461],{"class":46}," { MsfeNodeViewProps } ",[36,463,50],{"class":42},[36,465,466],{"class":53}," '@quirekit\u002Fmsfe-core'\n",[36,468,469,471,474,476],{"class":38,"line":57},[36,470,43],{"class":42},[36,472,473],{"class":46}," { MsfeEditorContent, ReactNodeViewRenderer } ",[36,475,50],{"class":42},[36,477,54],{"class":53},[36,479,480,482,484,486],{"class":38,"line":70},[36,481,43],{"class":42},[36,483,62],{"class":46},[36,485,50],{"class":42},[36,487,67],{"class":53},[36,489,490,492,495,497],{"class":38,"line":85},[36,491,43],{"class":42},[36,493,494],{"class":46}," { Status } ",[36,496,50],{"class":42},[36,498,499],{"class":53}," '.\u002Fstatus'\n",[36,501,502],{"class":38,"line":92},[36,503,89],{"emptyLinePlaceholder":88},[36,505,506,509,512,515,518,522,524,527,530,533],{"class":38,"line":115},[36,507,508],{"class":42},"interface",[36,510,511],{"class":105}," StatusAttributes",[36,513,514],{"class":46}," { ",[36,516,517],{"class":42},"readonly",[36,519,521],{"class":520},"sde8i"," value",[36,523,102],{"class":42},[36,525,526],{"class":53}," 'todo'",[36,528,529],{"class":42}," |",[36,531,532],{"class":53}," 'done'",[36,534,535],{"class":46}," }\n",[36,537,538,541,544,547,550,553,555,557,559,561,564,566,569],{"class":38,"line":127},[36,539,540],{"class":42},"function",[36,542,543],{"class":105}," StatusView",[36,545,546],{"class":46},"({ ",[36,548,549],{"class":520},"nodeView",[36,551,552],{"class":46}," }",[36,554,102],{"class":42},[36,556,514],{"class":46},[36,558,549],{"class":520},[36,560,102],{"class":42},[36,562,563],{"class":105}," MsfeNodeViewProps",[36,565,309],{"class":46},[36,567,568],{"class":105},"StatusAttributes",[36,570,571],{"class":46},"> }) {\n",[36,573,574,576,578,581,584,586,589,592,594,597,599,602,605],{"class":38,"line":138},[36,575,186],{"class":42},[36,577,189],{"class":46},[36,579,580],{"class":380},"button",[36,582,583],{"class":105}," disabled",[36,585,196],{"class":42},[36,587,588],{"class":46},"{nodeView.readOnly} ",[36,590,591],{"class":105},"onClick",[36,593,196],{"class":42},[36,595,596],{"class":46},"{() ",[36,598,338],{"class":42},[36,600,601],{"class":46}," nodeView.",[36,603,604],{"class":105},"updateAttributes",[36,606,607],{"class":46},"({\n",[36,609,610,613,616,618,621,623,626,628],{"class":38,"line":144},[36,611,612],{"class":46},"    value: nodeView.attrs.value ",[36,614,615],{"class":42},"===",[36,617,532],{"class":53},[36,619,620],{"class":42}," ?",[36,622,526],{"class":53},[36,624,625],{"class":42}," :",[36,627,532],{"class":53},[36,629,124],{"class":46},[36,631,632,635,637],{"class":38,"line":150},[36,633,634],{"class":46},"  })}>{nodeView.attrs.value}\u003C\u002F",[36,636,580],{"class":380},[36,638,639],{"class":46},">\n",[36,641,642],{"class":38,"line":155},[36,643,147],{"class":46},[36,645,646],{"class":38,"line":168},[36,647,648],{"class":158},"\u002F\u002F Status is the framework-independent extension from the custom-node tutorial.\n",[36,650,651,653,656,658,661,664],{"class":38,"line":183},[36,652,95],{"class":42},[36,654,655],{"class":98}," StatusWithUI",[36,657,109],{"class":42},[36,659,660],{"class":46}," Status.",[36,662,663],{"class":105},"extend",[36,665,607],{"class":46},[36,667,668,671,674,677,680],{"class":38,"line":202},[36,669,670],{"class":105},"  addNodeView",[36,672,673],{"class":46},"() { ",[36,675,676],{"class":42},"return",[36,678,679],{"class":105}," ReactNodeViewRenderer",[36,681,682],{"class":46},"(StatusView) },\n",[36,684,685],{"class":38,"line":392},[36,686,687],{"class":46},"})\n",[36,689,690],{"class":38,"line":413},[36,691,89],{"emptyLinePlaceholder":88},[36,693,694,696,698],{"class":38,"line":419},[36,695,159],{"class":158},[36,697,162],{"class":42},[36,699,700],{"class":158}," A mounted editor using the custom React node view. *\u002F\n",[36,702,704,706,708,711],{"class":38,"line":703},18,[36,705,171],{"class":42},[36,707,174],{"class":42},[36,709,710],{"class":105}," StatusEditor",[36,712,180],{"class":46},[36,714,716,718,720,722,724,726],{"class":38,"line":715},19,[36,717,186],{"class":42},[36,719,189],{"class":46},[36,721,20],{"class":98},[36,723,99],{"class":105},[36,725,196],{"class":42},[36,727,728],{"class":46},"{{\n",[36,730,732,734,737,740,743,746,748],{"class":38,"line":731},20,[36,733,346],{"class":46},[36,735,736],{"class":53},"'# Status",[36,738,739],{"class":98},"\\n\\n",[36,741,742],{"class":53},"[[todo]]'",[36,744,745],{"class":46},", mode: ",[36,747,133],{"class":53},[36,749,124],{"class":46},[36,751,753],{"class":38,"line":752},21,[36,754,755],{"class":46},"    extensions: [StarterKit, StatusWithUI],\n",[36,757,759],{"class":38,"line":758},22,[36,760,761],{"class":46},"  }} \u002F>\n",[36,763,765],{"class":38,"line":764},23,[36,766,147],{"class":46},[15,768,769,770,223,773,776,777,782],{},"Copy the framework-independent ",[18,771,772],{},"status.ts",[18,774,775],{},"types.ts"," from the ",[778,779,781],"a",{"href":780},"\u002Fen\u002Fextensions\u002Fcustom-node","node tutorial"," beside this component first. Replace the illustrative native button with your React component library. Generic controls use ReactComponentViewRenderer and receive view, not nodeView.",[10,784,786],{"id":785},"updates-and-cleanup","Updates and cleanup",[15,788,789],{},"Saving, modes and external changes use the same instance APIs. Avoid feeding whole-document changes back into options and triggering remounts. Re-establish host subscriptions for each session and clean up effects rather than retaining stale editor references.",[15,791,792],{},"React snippets describe the public integration. This site's interactive examples run in Vue; consuming React apps should verify StrictMode, SSR and routing themselves.",[794,795,796],"style",{},"html pre.shiki code .sDD9d, html code.shiki .sDD9d{--shiki-default:#D73A49;--shiki-light:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVPnS, html code.shiki .sVPnS{--shiki-default:#24292E;--shiki-light:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s-HEn, html code.shiki .s-HEn{--shiki-default:#032F62;--shiki-light:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sUNQB, html code.shiki .sUNQB{--shiki-default:#005CC5;--shiki-light:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sgQzM, html code.shiki .sgQzM{--shiki-default:#6F42C1;--shiki-light:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sqmzS, html code.shiki .sqmzS{--shiki-default:#6A737D;--shiki-light:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .su0ou, html code.shiki .su0ou{--shiki-default:#22863A;--shiki-light:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sde8i, html code.shiki .sde8i{--shiki-default:#E36209;--shiki-light:#E36209;--shiki-dark:#FFAB70}",{"title":32,"searchDepth":57,"depth":57,"links":798},[799,800,801,802,803],{"id":12,"depth":57,"text":13},{"id":207,"depth":57,"text":208},{"id":424,"depth":57,"text":425},{"id":443,"depth":57,"text":444},{"id":785,"depth":57,"text":786},"Mount the editor and portals through the React adapter while preserving application context.","md",[807,808,809],"React","StrictMode","Portal","en",{},"\u002Fen\u002Fguide\u002Freact","Integration",{"title":5,"description":804},"guide\u002Freact.en","lA5hYNwZcnRXf16ttcrrqlo1jnMm2pTTd1Dhu1xAnDc",1793046311992]