[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"url-settings":86,"blog-\u002Fblog\u002Fremult-typescript-react-fullstack-app":671,"blog-author-\u002Fblog\u002Fremult-typescript-react-fullstack-app":1345,"i-material-symbols:arrow-back-rounded":1360},{"id":4,"extension":5,"footer":6,"header":73,"meta":83,"stem":84,"__hash__":85},"navigation\u002Fdata\u002Fshared\u002Fnavigation.yml","yml",{"brand":7,"columns":13,"legal":63},{"name":8,"tagline":9,"downloadCta":10},"Pieces","The memory layer for modern work.",{"label":11,"href":12},"All downloads","\u002Fdownloads",[14,26,39,51],{"title":15,"links":16},"Product",[17,20,23],{"label":18,"href":19},"Pieces Desktop","\u002F",{"label":21,"href":22},"Pieces Enterprise","\u002Fenterprise",{"label":24,"href":25},"Pieces MCP","\u002Fmcp",{"title":27,"links":28},"Resources",[29,33,36],{"label":30,"href":31,"external":32},"Documentation","url:docs.home",true,{"label":34,"href":35},"Blog","\u002Fblog",{"label":37,"href":38,"external":32},"GitHub","url:github.org",{"title":40,"links":41},"Community",[42,45,48],{"label":43,"href":44,"external":32},"Discord","url:social.discord",{"label":46,"href":47,"external":32},"X \u002F Twitter","url:social.x",{"label":49,"href":50,"external":32},"LinkedIn","url:social.linkedin",{"title":52,"links":53},"Company",[54,57,60],{"label":55,"href":56},"About","\u002Fabout",{"label":58,"href":59},"Updates","\u002Fupdates",{"label":61,"href":62},"Contact","\u002Fcontact",[64,67,70],{"label":65,"href":66},"Privacy Policy","\u002Flegal\u002Fprivacy",{"label":68,"href":69},"Refund Policy","\u002Flegal\u002Frefund",{"label":71,"href":72},"Terms of Service","\u002Flegal\u002Fterms",{"links":74,"signIn":75,"contact":78,"cta":80},[],{"label":76,"href":77},"Manage account","url:portal.home",{"label":79,"href":62},"Get in touch",{"label":81,"href":82},"Download","url:routes.downloads",{},"data\u002Fshared\u002Fnavigation","XlSALC7vYCXHcmcXnjJViwIZKsqnQWF4bn4o9RrcSBE",{"id":87,"extension":5,"links":88,"meta":668,"stem":669,"__hash__":670},"urlSettings\u002Fdata\u002Fshared\u002Furls.yml",[89,93,97,101,105,109,113,117,121,125,129,133,137,141,145,149,153,157,161,165,169,173,177,181,185,189,192,196,200,204,208,212,216,220,224,228,232,236,240,244,248,252,256,260,264,268,272,276,280,284,288,292,296,300,303,307,311,314,318,322,326,330,334,338,342,346,350,354,358,362,366,370,374,378,382,386,390,394,398,402,406,410,414,418,422,426,430,434,438,442,446,450,454,458,461,465,469,473,477,481,485,488,491,494,497,501,505,508,512,516,520,524,528,532,536,540,544,548,552,556,559,563,567,571,575,579,582,585,588,592,595,599,603,607,611,614,618,622,625,628,631,635,639,643,646,649,652,656,660,664],{"key":90,"label":91,"href":92},"downloads.desktop","Desktop download page","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdownload",{"key":94,"label":95,"href":96},"downloads.macOS.dmgArm64","macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg-arm64\u002Fdownload",{"key":98,"label":99,"href":100},"downloads.macOS.dmgIntel","macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fdmg\u002Fdownload",{"key":102,"label":103,"href":104},"downloads.macOS.pkg","macOS PKG","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg\u002Fdownload",{"key":106,"label":107,"href":108},"downloads.macOS.universal","Desktop macOS Universal","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fmacos-universal\u002Fdownload",{"key":110,"label":111,"href":112},"downloads.macOS.pkgArm64","Desktop macOS PKG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pfd-arm64\u002Fdownload",{"key":114,"label":115,"href":116},"downloads.macOS.pkgIntel","Desktop macOS PKG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pfd\u002Fdownload",{"key":118,"label":119,"href":120},"downloads.windows.appinstaller","Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fpieces_for_x.appinstaller",{"key":122,"label":123,"href":124},"downloads.windows.exe","Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fwindows-exe\u002Fdownload",{"key":126,"label":127,"href":128},"downloads.windows.msix","Windows MSIX","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fwindows-msix\u002Fdownload",{"key":130,"label":131,"href":132},"downloads.windows.suiteManager","Windows Suite Manager","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_suite_windows\u002Fappinstaller\u002Fdownload",{"key":134,"label":135,"href":136},"downloads.linux.flatpakRepo","Linux Flatpak repository","https:\u002F\u002Fbuilds.pieces.app\u002Fpieces-flatpak-repo\u002Fpieces-flatpak.flatpakrepo",{"key":138,"label":139,"href":140},"downloads.linux.snapDesktop","Linux Snap Desktop","https:\u002F\u002Fsnapcraft.io\u002Fpieces-for-developers",{"key":142,"label":143,"href":144},"downloads.linux.snapPiecesOS","Linux Snap PiecesOS","https:\u002F\u002Fsnapcraft.io\u002Fpieces-os",{"key":146,"label":147,"href":148},"downloads.linux.snap","Desktop Linux Snap package","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fpieces_for_x\u002Fsnap\u002Fdownload",{"key":150,"label":151,"href":152},"downloads.piecesOS.macOS.dmgArm64","PiecesOS macOS DMG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg-arm64\u002Fdownload",{"key":154,"label":155,"href":156},"downloads.piecesOS.macOS.dmgIntel","PiecesOS macOS DMG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fdmg\u002Fdownload",{"key":158,"label":159,"href":160},"downloads.piecesOS.macOS.universal","PiecesOS macOS Universal","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fmacos-universal\u002Fdownload",{"key":162,"label":163,"href":164},"downloads.piecesOS.macOS.pkgArm64","PiecesOS macOS PKG Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pos-launch-only-arm64\u002Fdownload",{"key":166,"label":167,"href":168},"downloads.piecesOS.macOS.pkgIntel","PiecesOS macOS PKG Intel","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-pos-launch-only\u002Fdownload",{"key":170,"label":171,"href":172},"downloads.piecesOS.windows.appinstaller","PiecesOS Windows App Installer","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fappinstaller\u002Fos_server.appinstaller",{"key":174,"label":175,"href":176},"downloads.piecesOS.windows.exe","PiecesOS Windows EXE","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fwindows-exe\u002Fdownload",{"key":178,"label":179,"href":180},"downloads.piecesOS.windows.msix","PiecesOS Windows MSIX","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fwindows-msix\u002Fdownload",{"key":182,"label":183,"href":184},"downloads.piecesOS.linux.snap","PiecesOS Linux Snap package","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fos_server\u002Fsnap\u002Fdownload",{"key":186,"label":187,"href":188},"downloads.combined.macOS.appleSilicon","Combined macOS installer Apple Silicon","https:\u002F\u002Fbuilds.pieces.app\u002Fstages\u002Fproduction\u002Fmacos_packaging\u002Fpkg-arm64\u002Fdownload",{"key":190,"label":191,"href":104},"downloads.combined.macOS.intel","Combined macOS installer Intel",{"key":193,"label":194,"href":195},"downloads.guides.macOS","macOS installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fmacos-installation-guide",{"key":197,"label":198,"href":199},"downloads.guides.windows","Windows installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Fwindows-installation-guide",{"key":201,"label":202,"href":203},"downloads.guides.linux","Linux installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Flinux-installation-guide",{"key":205,"label":206,"href":207},"downloads.guides.linuxFlatpak","Linux Flatpak installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Flinux-installation-guide\u002Fflatpak",{"key":209,"label":210,"href":211},"downloads.guides.linuxSnap","Linux Snap installation guide","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Flinux-installation-guide\u002Fsnap",{"key":213,"label":214,"href":215},"downloads.guides.piecesOS","PiecesOS manual installation","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fmanual-installation",{"key":217,"label":218,"href":219},"extensions.chrome","Chrome extension","https:\u002F\u002Fchrome.google.com\u002Fwebstore\u002Fdetail\u002Fpieces-save-code-snippets\u002Figbgibhbfonhmjlechmeefimncpekepm",{"key":221,"label":222,"href":223},"extensions.firefox","Firefox add-on","https:\u002F\u002Faddons.mozilla.org\u002Fen-US\u002Ffirefox\u002Faddon\u002Fpieces-save-code-from-the-web\u002F",{"key":225,"label":226,"href":227},"extensions.edge","Edge add-on","https:\u002F\u002Fmicrosoftedge.microsoft.com\u002Faddons\u002Fdetail\u002Fpieces-save-code-snippet\u002Fhglfimcdgonaeeobjckfdabcldfidmim",{"key":229,"label":230,"href":231},"extensions.vscode","VS Code extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.pieces-vscode",{"key":233,"label":234,"href":235},"extensions.visualStudio","Visual Studio extension","https:\u002F\u002Fmarketplace.visualstudio.com\u002Fitems?itemName=MeshIntelligentTechnologiesInc.PiecesVisualStudio",{"key":237,"label":238,"href":239},"extensions.jetbrains","JetBrains plugin","https:\u002F\u002Fplugins.jetbrains.com\u002Fplugin\u002F17328-pieces--save-search-share--reuse-code-snippets",{"key":241,"label":242,"href":243},"extensions.obsidian","Obsidian plugin","https:\u002F\u002Fobsidian.md\u002Fplugins?id=pieces-for-developers",{"key":245,"label":246,"href":247},"extensions.sublime","Sublime package","https:\u002F\u002Fpackagecontrol.io\u002Fpackages\u002FPieces",{"key":249,"label":250,"href":251},"extensions.neovim","Neovim plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neo_vim",{"key":253,"label":254,"href":255},"extensions.jupyterlab","JupyterLab plugin","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fjupyterlab-pieces",{"key":257,"label":258,"href":259},"extensions.cli","Pieces CLI","https:\u002F\u002Fpypi.org\u002Fproject\u002Fpieces-cli\u002F",{"key":261,"label":262,"href":263},"docs.home","Documentation home","https:\u002F\u002Fdocs.pieces.app",{"key":265,"label":266,"href":267},"docs.getStarted","Get started docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces",{"key":269,"label":270,"href":271},"docs.api","API docs","https:\u002F\u002Fdocs.pieces.app\u002Fapi",{"key":273,"label":274,"href":275},"docs.desktop.overview","Desktop overview","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop",{"key":277,"label":278,"href":279},"docs.desktop.onboarding","Desktop onboarding","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fonboarding",{"key":281,"label":282,"href":283},"docs.desktop.timeline","Desktop timeline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Ftimeline",{"key":285,"label":286,"href":287},"docs.desktop.summaries","Desktop summaries docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fsingle-click-summaries",{"key":289,"label":290,"href":291},"docs.desktop.search","Desktop conversational search docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fconversational-search",{"key":293,"label":294,"href":295},"docs.desktop.drive","Desktop drive docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fdrive",{"key":297,"label":298,"href":299},"docs.desktop.account","Desktop account settings docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fdesktop\u002Fconfiguration\u002Faccount",{"key":301,"label":302,"href":92},"docs.desktop.download","Desktop download docs",{"key":304,"label":305,"href":306},"docs.piecesOS.overview","PiecesOS overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies",{"key":308,"label":309,"href":310},"docs.piecesOS.details","PiecesOS details docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os",{"key":312,"label":313,"href":215},"docs.piecesOS.install","PiecesOS install docs",{"key":315,"label":316,"href":317},"docs.piecesOS.quickMenu","PiecesOS quick menu docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Fquick-menu",{"key":319,"label":320,"href":321},"docs.piecesOS.storage","On-device storage docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fon-device-storage",{"key":323,"label":324,"href":325},"docs.piecesOS.troubleshooting","PiecesOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fcore-dependencies\u002Fpieces-os\u002Ftroubleshooting",{"key":327,"label":328,"href":329},"docs.mcp.overview","MCP overview docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp",{"key":331,"label":332,"href":333},"docs.mcp.cursor","MCP Cursor docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcursor",{"key":335,"label":336,"href":337},"docs.mcp.vscode","MCP VS Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fvs-code",{"key":339,"label":340,"href":341},"docs.mcp.claudeDesktop","MCP Claude Desktop docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-desktop",{"key":343,"label":344,"href":345},"docs.mcp.claudeCode","MCP Claude Code docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-code",{"key":347,"label":348,"href":349},"docs.mcp.claudeCowork","MCP Claude Cowork docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fclaude-cowork",{"key":351,"label":352,"href":353},"docs.mcp.githubCopilot","MCP GitHub Copilot docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgithub-copilot",{"key":355,"label":356,"href":357},"docs.mcp.goose","MCP Goose docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoose",{"key":359,"label":360,"href":361},"docs.mcp.windsurf","MCP Windsurf docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fwindsurf",{"key":363,"label":364,"href":365},"docs.mcp.zed","MCP Zed docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fzed",{"key":367,"label":368,"href":369},"docs.mcp.jetbrains","MCP JetBrains docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fjetbrains-ides",{"key":371,"label":372,"href":373},"docs.mcp.continueDev","MCP Continue docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcontinue-dev",{"key":375,"label":376,"href":377},"docs.mcp.cline","MCP Cline docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fcline",{"key":379,"label":380,"href":381},"docs.mcp.raycast","MCP Raycast docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fraycast",{"key":383,"label":384,"href":385},"docs.mcp.rovoDevCli","MCP Rovo Dev CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Frovo-dev-cli",{"key":387,"label":388,"href":389},"docs.mcp.openaiCodexCli","MCP OpenAI Codex CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenai-codex-cli",{"key":391,"label":392,"href":393},"docs.mcp.googleGeminiCli","MCP Google Gemini CLI docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fgoogle-gemini-cli",{"key":395,"label":396,"href":397},"docs.mcp.amazonQ","MCP Amazon Q docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Famazon-q-developer",{"key":399,"label":400,"href":401},"docs.mcp.chatgptDev","MCP ChatGPT Developer Mode docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fchatgpt-developer-mode",{"key":403,"label":404,"href":405},"docs.mcp.openclaw","MCP OpenClaw docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fopenclaw",{"key":407,"label":408,"href":409},"docs.mcp.mcpRemote","MCP Remote docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fmcp-remote",{"key":411,"label":412,"href":413},"docs.mcp.ngrok","MCP ngrok docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmcp\u002Fngrok-setup",{"key":415,"label":416,"href":417},"docs.troubleshooting.macOS","macOS troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fmacos",{"key":419,"label":420,"href":421},"docs.troubleshooting.windows","Windows troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Fwindows",{"key":423,"label":424,"href":425},"docs.troubleshooting.linux","Linux troubleshooting docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fmeet-pieces\u002Ftroubleshooting\u002Flinux",{"key":427,"label":428,"href":429},"docs.privacy","Privacy and security docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fprivacy-security-your-data",{"key":431,"label":432,"href":433},"docs.support","Support docs","https:\u002F\u002Fdocs.pieces.app\u002Fproducts\u002Fsupport",{"key":435,"label":436,"href":437},"portal.home","Pieces portal","https:\u002F\u002Fportal.pieces.app",{"key":439,"label":440,"href":441},"site.home","Website home","https:\u002F\u002Fpieces.app",{"key":443,"label":444,"href":445},"site.about","About page","https:\u002F\u002Fpieces.app\u002Fabout",{"key":447,"label":448,"href":449},"site.features","Features page","https:\u002F\u002Fpieces.app\u002Ffeatures",{"key":451,"label":452,"href":453},"site.plugins","Plugins page","https:\u002F\u002Fpieces.app\u002Fplugins",{"key":455,"label":456,"href":457},"site.contact","Contact page","https:\u002F\u002Fpieces.app\u002Fcontact",{"key":459,"label":58,"href":460},"site.updates","https:\u002F\u002Fpieces.app\u002Fupdates",{"key":462,"label":463,"href":464},"site.news","News","https:\u002F\u002Fpieces.app\u002Fnews",{"key":466,"label":467,"href":468},"site.events","Community events","https:\u002F\u002Fpieces.app\u002Fcommunity\u002Fevents",{"key":470,"label":471,"href":472},"site.userStories","User stories","https:\u002F\u002Fpieces.app\u002Fuser-stories",{"key":474,"label":475,"href":476},"site.academy","Academy","https:\u002F\u002Fpieces.app\u002Flearn\u002Facademy",{"key":478,"label":479,"href":480},"site.support","Website support","https:\u002F\u002Fpieces.app\u002Fsupport",{"key":482,"label":483,"href":484},"site.standup","Standup","https:\u002F\u002Fpieces.app\u002Fstandup",{"key":486,"label":34,"href":487},"site.blog","https:\u002F\u002Fcode.pieces.app\u002Fblog",{"key":489,"label":43,"href":490},"social.discord","https:\u002F\u002Fdiscord.gg\u002Fgetpieces",{"key":492,"label":46,"href":493},"social.x","https:\u002F\u002Fx.com\u002Fgetpieces",{"key":495,"label":496,"href":493},"social.twitter","Twitter",{"key":498,"label":499,"href":500},"social.instagram","Instagram","https:\u002F\u002Fwww.instagram.com\u002Fgetpieces\u002F",{"key":502,"label":503,"href":504},"social.tiktok","TikTok","https:\u002F\u002Fwww.tiktok.com\u002F@getpieces",{"key":506,"label":49,"href":507},"social.linkedin","https:\u002F\u002Fwww.linkedin.com\u002Fcompany\u002Fgetpieces\u002F",{"key":509,"label":510,"href":511},"social.youtube","YouTube","https:\u002F\u002Fyoutube.com\u002F@getpieces",{"key":513,"label":514,"href":515},"github.org","GitHub organization","https:\u002F\u002Fgithub.com\u002Fpieces-app",{"key":517,"label":518,"href":519},"github.support","GitHub support","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport",{"key":521,"label":522,"href":523},"github.issues","GitHub issues","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fissues",{"key":525,"label":526,"href":527},"github.discussions","GitHub discussions","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fsupport\u002Fdiscussions",{"key":529,"label":530,"href":531},"github.documentation","GitHub documentation","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fdocumentation",{"key":533,"label":534,"href":535},"github.opensource","GitHub open source","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fopensource",{"key":537,"label":538,"href":539},"github.sdks.python","Python SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-python",{"key":541,"label":542,"href":543},"github.sdks.typescript","TypeScript SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-typescript",{"key":545,"label":546,"href":547},"github.sdks.dart","Dart SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-dart",{"key":549,"label":550,"href":551},"github.sdks.kotlin","Kotlin SDK","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fpieces-os-client-sdk-for-kotlin",{"key":553,"label":554,"href":555},"github.plugins.obsidian","Obsidian plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fobsidian-pieces",{"key":557,"label":558,"href":255},"github.plugins.jupyterlab","JupyterLab plugin repository",{"key":560,"label":561,"href":562},"github.plugins.sublime","Sublime plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_sublime",{"key":564,"label":565,"href":566},"github.plugins.neovim","Neovim plugin repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fplugin_neovim",{"key":568,"label":569,"href":570},"github.cliAgent","CLI agent repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fcli-agent",{"key":572,"label":573,"href":574},"github.mcpDart","MCP Dart repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fmcp_dart",{"key":576,"label":577,"href":578},"github.awesomePieces","Awesome Pieces repository","https:\u002F\u002Fgithub.com\u002Fpieces-app\u002Fawesome-pieces",{"key":580,"label":581,"href":66},"legal.privacyPolicy","Privacy policy",{"key":583,"label":584,"href":69},"legal.refundPolicy","Refund policy",{"key":586,"label":587,"href":72},"legal.terms","Terms",{"key":589,"label":590,"href":591},"legal.security","Legal security","https:\u002F\u002Fpieces.app\u002Flegal\u002Fsecurity",{"key":593,"label":594,"href":511},"videos.youtubeChannel","YouTube channel",{"key":596,"label":597,"href":598},"videos.gettingStartedDesktop","Getting started desktop video","https:\u002F\u002Fyoutu.be\u002FdUr1lRM_TYk",{"key":600,"label":601,"href":602},"videos.snippetDiscoveryxR","Snippet discovery video","https:\u002F\u002Fyoutu.be\u002FG6vb1USw-30",{"key":604,"label":605,"href":606},"sales.bookACall","Book a sales call","https:\u002F\u002Fcalendar.app.google\u002FWVUDtUfNy5Vst3sH7",{"key":608,"label":609,"href":610},"sales.enterprise","Enterprise form","https:\u002F\u002Fgetpieces.typeform.com\u002Fto\u002FaVQFTvpE",{"key":612,"label":613,"href":527},"sales.feedback","Feedback discussions",{"key":615,"label":616,"href":617},"sales.earlyAccess","Early access form","https:\u002F\u002Fgetpieces.typeform.com\u002Fearlyaccess",{"key":619,"label":620,"href":621},"sales.supportEmail","Support email","mailto:support@pieces.app",{"key":623,"label":624,"href":19},"routes.home","Home route",{"key":626,"label":627,"href":56},"routes.about","About route",{"key":629,"label":630,"href":12},"routes.downloads","Downloads route",{"key":632,"label":633,"href":634},"routes.thanks","Post-install thanks route","\u002Finstall\u002Fthanks",{"key":636,"label":637,"href":638},"routes.pricing","Pricing route","\u002Fpricing",{"key":640,"label":641,"href":642},"cta.freeTrial","Start free trial CTA (Pieces Pro) — used by \u002Fcampaigns\u002F* landing pages","https:\u002F\u002Fcampaigns.pieces.app\u002F",{"key":644,"label":645,"href":22},"routes.enterprise","Enterprise route",{"key":647,"label":648,"href":62},"routes.contact","Contact route",{"key":650,"label":651,"href":59},"routes.updates","Updates route",{"key":653,"label":654,"href":655},"routes.migrationError","Migration error route","\u002Fmigration\u002Ferror",{"key":657,"label":658,"href":659},"routes.authenticated","Post-login authenticated route","\u002Fauth\u002Fsigned-in",{"key":661,"label":662,"href":663},"routes.signed-out","Signed out route","\u002Fauth\u002Fsigned-out",{"key":665,"label":666,"href":667},"routes.authentication-error","Authentication error route","\u002Fauth\u002Ferror",{},"data\u002Fshared\u002Furls","2AvfSWI6bZACH5Kr1ZS3txMLjteRjCjbDR_ul1uRZ9Q",{"id":672,"title":673,"author":674,"authorPhoto":675,"authorPhotoAlt":676,"authorSlug":677,"body":678,"buttonText":1332,"buttonUrl":1332,"category":1333,"date":1334,"description":1335,"draft":1336,"editorsPick":1336,"extension":1337,"featured":1336,"image":1338,"imageAlt":1339,"meta":1340,"navigation":32,"ogImage":1332,"ogImageAlt":1332,"path":1341,"seo":1342,"stem":1343,"tags":1332,"__hash__":1344},"blog\u002Fblog\u002Fremult-typescript-react-fullstack-app.md","Remult, TypeScript, and React: The Perfect Combo for a Full-stack App","Ekekenta Clinton","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fremult-typescript-react-fullstack-app\u002Fauthor.png","Ekekenta Clinton.","ekekenta-clinton",{"type":679,"value":680,"toc":1315},"minimark",[681,685,690,693,698,701,717,721,724,759,763,766,777,780,786,789,795,798,802,821,827,830,834,837,843,858,864,875,885,891,902,908,915,921,948,954,960,966,969,975,979,982,988,994,1000,1025,1029,1036,1042,1048,1080,1088,1094,1101,1105,1108,1122,1128,1148,1160,1166,1170,1173,1179,1186,1192,1216,1223,1229,1240,1243,1249,1260,1266,1276,1283,1290,1293,1299,1303,1306],[682,683,684],"p",{},"A full-stack application is made up of frameworks, libraries, and tools for developing two separate domain applications: the backend and the frontend. Building these two applications can be very hectic, especially if the frameworks are developed from different programming languages or if one developer is working on both sides of the application. However, we’re grateful to the JavaScript developers for creating backend frameworks that allow us to use JavaScript to build both the client and server sides of our application. The Remult developers expanded on this concept by developing a framework that allows us to build both the client and server sides of an application in a single project rather than having two separate projects. We’ll explore how this can be implemented in this tutorial. Then, we’ll learn how to build a full-stack application with TypeScript and React using Remult.",[686,687,689],"h2",{"id":688},"what-is-remult","What is Remult?",[682,691,692],{},"Remult is a full-stack TypeScript CRUD framework with a frontend type-safe API client and a backend ORM that uses entities as a single source of truth for your API. It saves developers time by abstracting all repetitive or poorly designed code, resulting in a more flexible web application. It makes full-stack app development easier by using only TypeScript code that’s easy to follow and refactor, and it fits well into any existing or new project.",[694,695,697],"h3",{"id":696},"why-use-remult","Why Use Remult?",[682,699,700],{},"Below are some of the reasons developers use Remult:",[702,703,704,708,711,714],"ul",{},[705,706,707],"li",{},"It has a secure auto-generated TypeScript API model and classes that are consumed by frontend type-safe queries that can also be used as third-party applications.",[705,709,710],{},"It’s a simple CRUD application that interacts with the database directly from the frontend and does not require any boilerplate, so data transformations, validations, and CRUD events are easily controlled.",[705,712,713],{},"It uses a type-safe coding style to find and manipulate data on both the backend and frontend code.",[705,715,716],{},"It eliminates redundant and error-prone duplication with model metadata and declarative code that impacts both the frontend and the backend.",[686,718,720],{"id":719},"prerequisites","Prerequisites",[682,722,723],{},"This is a hands-on tutorial, so to follow along, be sure that you’ve done these things:",[702,725,726,737,745],{},[705,727,728,729,736],{},"Installed ",[730,731,735],"a",{"href":732,"rel":733},"https:\u002F\u002Fnodejs.org\u002F",[734],"nofollow","Node.js"," V14 or later",[705,738,739,744],{},[730,740,743],{"href":741,"rel":742},"https:\u002F\u002Fwww.googleadservices.com\u002Fpagead\u002Faclk?sa=L&ai=DChcSEwi3vPfd1bP6AhVKA4sKHQbnBWsYABAAGgJlZg&ohost=www.google.com&cid=CAESbOD2X-a6kboxXHXG3izU05eObOfzWxIrNJfa2IeMsImyvcs6l3QguTUf9pPYUNi6jbMn8Rz6TU0SWme6fbL3uUvtL6HyEzJhKOuYte37s4tYRlvpOV4oP-eB8VpRxFGnQhm1rArwTetsZzURSg&sig=AOD64_3r0aVg59iknPszkLuo3XbggSzTLA&q&adurl&ved=2ahUKEwjg2O_d1bP6AhUfgf0HHTn-AqMQ0Qx6BAgIEAE",[734],"MongoDB"," Installed your database",[705,746,747,748,753,754],{},"Prior knowledge of ",[730,749,752],{"href":750,"rel":751},"https:\u002F\u002Fwww.typescriptlang.org\u002F",[734],"TypeScript"," and ",[730,755,758],{"href":756,"rel":757},"https:\u002F\u002Freactjs.org\u002F",[734],"React",[686,760,762],{"id":761},"project-setup","Project Setup",[682,764,765],{},"Without further ado, let’s scaffold a Remult React full stack application using Vite by running the command below:",[767,768,773],"pre",{"className":769,"code":771,"language":772},[770],"language-text","npm create -y vite@latest remult-react-blog -- --template react-tscd remult-react-blog\n","text",[774,775,771],"code",{"__ignoreMap":776},"",[682,778,779],{},"The above commands will scaffold a new React project with the following folder structure:",[767,781,784],{"className":782,"code":783,"language":772},[770],"📦remult-react-blog\n┣ 📂public\n┃ ┗ 📜vite.svg\n┣ 📂src\n┃ ┣ 📂assets\n┃ ┃ ┗ 📜react.svg\n┃ ┣ 📜App.css\n┃ ┣ 📜App.tsx\n┃ ┣ 📜index.css\n┃ ┣ 📜main.tsx\n┃ ┗ 📜vite-env.d.ts\n┣ 📜.gitignore\n┣ 📜index.html\n┣ 📜package.json\n┣ 📜tsconfig.json\n┣ 📜tsconfig.node.json\n┗ 📜vite.config.ts\n",[774,785,783],{"__ignoreMap":776},[682,787,788],{},"In order to get your React application running, change the directory into the project folder, install the required packages, and start the application by running the commands below:",[767,790,793],{"className":791,"code":792,"language":772},[770],"cd remult-react-blognpm install\n",[774,794,792],{"__ignoreMap":776},[682,796,797],{},"Now, let’s set up the backend of the application.",[694,799,801],{"id":800},"install-dependencies","Install Dependencies",[682,803,804,805,809,810,812,813,816,817,820],{},"The first step in setting up the backend is to install the required dependencies. We’ll use ",[806,807,808],"strong",{},"Express"," for the backend, and since Remult creates both the backend and frontend in one project, we’ll need to have them running concurrently. So run the commands to install ",[806,811,808],{}," and the ",[806,814,815],{},"Remult"," SDK, and use ",[774,818,819],{},"ts-node"," to run the application in development.",[767,822,825],{"className":823,"code":824,"language":772},[770],"npm i express remultnpm i --save-dev @types\u002Fexpress ts-node-dev concurrently\n",[774,826,824],{"__ignoreMap":776},[682,828,829],{},"Next, wait for the installation to be completed and proceed to creating the backend.",[694,831,833],{"id":832},"create-the-backend","Create the Backend",[682,835,836],{},"With the required packages for the backend setup installed, create a TypeScript config file and add the configurations below:",[767,838,841],{"className":839,"code":840,"language":772},[770],"{\n  \"extends\": \".\u002Ftsconfig.json\",\n  \"compilerOptions\": {\n    \"module\": \"commonjs\",\n    \"emitDecoratorMetadata\": true,\n    \"esModuleInterop\": true\n  }\n}\n",[774,842,840],{"__ignoreMap":776},[682,844,845,846,849,850,853,854,857],{},"Then, create a ",[806,847,848],{},"server"," folder in the ",[806,851,852],{},"src"," folder. Next, in the server folder, create an ",[806,855,856],{},"index.ts"," file and make an Express server with the code snippets below:",[767,859,862],{"className":860,"code":861,"language":772},[770],"import express from 'express';\nconst app = express();\napp.listen(3002, () => console.log(\"Server started\"));\n",[774,863,861],{"__ignoreMap":776},[682,865,866,867,870,871,874],{},"Since the application is using the Common.js module, you need to remove the ",[774,868,869],{},"\"type\": \"module\""," entry from the ",[806,872,873],{},"package.json"," file created by Vite.",[682,876,877,878,881,882,884],{},"Next, create an ",[806,879,880],{},"api.ts"," file on the ",[806,883,848],{}," and load Remult in the backend as an Express middleware with the code snippet below:",[767,886,889],{"className":887,"code":888,"language":772},[770],"import { remultExpress } from 'remult\u002Fremult-express';export const api = remultExpress();\n",[774,890,888],{"__ignoreMap":776},[682,892,893,894,897,898,901],{},"Then, register the Remult ",[806,895,896],{},"API"," middleware in the ",[806,899,900],{},"server\u002Findex.ts"," file with this code snippet:",[767,903,906],{"className":904,"code":905,"language":772},[770],"Import {api} from “.\u002Fapi”; app.use(api);\n",[774,907,905],{"__ignoreMap":776},[682,909,910,911,914],{},"Next, update the",[774,912,913],{},"tsconfig.json"," file to enable TypeScript decorators in the React.js full stack App with the entry below:",[767,916,919],{"className":917,"code":918,"language":772},[770],"\"experimentalDecorators\": true\n",[774,920,918],{"__ignoreMap":776},[682,922,923,924,929,930,935,936,938,939,943,944,947],{},"A Remult application is configured to run the frontend and backend from the same domain in production. However, in development, the API server listens to ",[730,925,928],{"href":926,"rel":927},"http:\u002F\u002Flocalhost:3002\u002F",[734],"http:\u002F\u002Flocalhost:3002",", while the frontend listens to port ",[730,931,934],{"href":932,"rel":933},"http:\u002F\u002Flocalhost:5173\u002F",[734],"http:\u002F\u002Flocalhost:5173",". Therefore, you need to use the Vite proxy feature to divert all requests to the ",[806,937,896],{}," to ",[730,940,941],{"href":941,"rel":942},"http:\u002F\u002Flocalhost:5173\u002Fapi",[734],". To do this, update the ",[806,945,946],{},"vite.confg.ts"," file with the code snippet below:",[767,949,952],{"className":950,"code":951,"language":772},[770],"export default defineConfig({\n  plugins: [react()],\n  server: { proxy: { '\u002Fapi': 'http:\u002F\u002Flocalhost:3002' } }\n})\n",[774,953,951],{"__ignoreMap":776},[682,955,956,957,959],{},"Next, update the ",[806,958,873],{}," file to add a start script to run the application in development with the entry below:",[767,961,964],{"className":962,"code":963,"language":772},[770],"\"dev\": \"concurrently -k -n \\\"API,WEB\\\" -c \\\"bgBlue.bold,bgGreen.bold\\\" \\\"ts-node-dev -P tsconfig.server.json src\u002Fserver\u002F\\\" \\\"vite\\\"\"\n",[774,965,963],{"__ignoreMap":776},[682,967,968],{},"Now, run the application with this command:",[767,970,973],{"className":971,"code":972,"language":772},[770],"npm run dev\n",[774,974,972],{"__ignoreMap":776},[694,976,978],{"id":977},"connect-to-a-database","Connect to a Database",[682,980,981],{},"With the backend created, let’s proceed to connecting the application to a MongoDB database to store our blog data. To get started, install the MongoDB package by running the command below:",[767,983,986],{"className":984,"code":985,"language":772},[770],"npm i mongdb\n",[774,987,985],{"__ignoreMap":776},[682,989,990,991,993],{},"Then, update the ",[806,992,900],{}," file and connect it to MongoDB with the following code snippet:",[767,995,998],{"className":996,"code":997,"language":772},[770],"import { remultExpress } from 'remult\u002Fremult-express';\nimport { MongoClient } from 'mongodb';\nimport { MongoDataProvider } from 'remult\u002Fremult-mongo';\napp.use(remultExpress({\n  dataProvider: async () => {\n    const client = new MongoClient(\"mongodb:\u002F\u002Flocalhost:27017\u002Flocal\");\n    await client.connect();\n    return new MongoDataProvider(client.db('blogs'), client);\n  }\n}));\n",[774,999,997],{"__ignoreMap":776},[682,1001,1002,1003,1006,1007,1010,1011,1014,1015,1017,1018,1021,1022,1024],{},"With the above code snippet, we imported the ",[774,1004,1005],{},"remultExpress","middleware, the ",[774,1008,1009],{},"MongoClient"," class and the ",[774,1012,1013],{},"MongoDataProvider"," class. The ",[774,1016,1005],{},"middlewaretakes a ",[774,1019,1020],{},"dataProvider","object as an argument, which allows us to connect to the database. We also created a client instance from the ",[774,1023,1009],{}," client class passing in the database URI and established a connection using the calling-the-client-connect method.",[686,1026,1028],{"id":1027},"create-a-blog-entity-with-remult","Create a Blog Entity with Remult",[682,1030,1031,1032,1035],{},"Now, with the connection to our database established, let's create a Blog entity to define and create our blog model. To do this, we’ll create a ",[806,1033,1034],{},"shared"," folder in the server folder. We’re saving it in this file because Remult classes are shared between the frontend and backend. This means we can access in the frontend any class created in the backend.",[682,1037,1038,1039,1041],{},"In the ",[806,1040,1034],{}," folder, create a blog.ts file and add the code below:",[767,1043,1046],{"className":1044,"code":1045,"language":772},[770],"import { Entity, Fields } from 'remult';\n@Entity('blogs', {\n  allowApiCrud: true\n})\nexport class Blogs {\n  @Fields.uuid()\n  id!: string;\n  @Fields.string()\n  title = '';\n  @Fields.string()\n  coverImage = '';\n  @Fields.string()\n  content = '';\n}\n",[774,1047,1045],{"__ignoreMap":776},[682,1049,1050,1051,753,1054,1057,1058,1060,1061,1064,1065,1067,1068,1071,1072,1075,1076,1079],{},"In the above code snippets, we imported the Remult ",[774,1052,1053],{},"Entity",[774,1055,1056],{},"Fields","decorators. We used the ",[774,1059,1053],{},"decorator to create a blogs entity, which will be translated to a model in our MongoDB base with the fields we defined in the ",[774,1062,1063],{},"Blogs"," class using the ",[774,1066,1056],{}," decorator. We also set",[774,1069,1070],{},"allowApiCrud","to ",[774,1073,1074],{},"true"," in the ",[774,1077,1078],{},"@Entity"," decorator to allow us to perform CRUD operations on this entity.",[682,1081,956,1082,1084,1085,1087],{},[806,1083,900],{}," file to register the entity in the ",[774,1086,1005],{}," middleware with this code snippet:",[767,1089,1092],{"className":1090,"code":1091,"language":772},[770],"import { Blogs } from '.\u002Fshared \u002Fblog';\napp.use(remultExpress({\n  dataProvider: async () => {\n    const client = new MongoClient(\"mongodb:\u002F\u002Flocalhost:27017\u002Flocal\");\n    await client.connect();\n    return new MongoDataProvider(client.db('test'), client);\n  },\n  entities: [Blogs],\n}));\n",[774,1093,1091],{"__ignoreMap":776},[682,1095,1096,1097,1100],{},"In the above code snippet, we imported the Blogs entity and registered it to the application in the array of ",[806,1098,1099],{},"entities"," object.",[686,1102,1104],{"id":1103},"create-crud-operations","Create CRUD Operations",[682,1106,1107],{},"Now let’s create our CRUD functions so that we Create, Read, Update and Delete a blog from our database.",[682,1109,1110,1111,1114,1115,1118,1119,1121],{},"First, you need to create a ",[774,1112,1113],{},"blogController.ts"," file in the ",[806,1116,1117],{},"server\u002Fshared"," folder. In the ",[774,1120,1113],{},"file, add the following code:",[767,1123,1126],{"className":1124,"code":1125,"language":772},[770],"import { BackendMethod, remult } from \"remult\";\nimport { Blogs } from \".\u002Fblog\";\nexport class BlogsController {\n  @BackendMethod({ allowed: true })\n  static async create(title: string, coverImage: string, content: string) {\n    const newBlog = await remult.repo(Blogs).save({ title, content, coverImage })\n   return newBlog\n  }\nstatic async getAll() {\n  return await this.blogRepo.find();\n}\nstatic async getOne(id: string) {\n  return await this.blogRepo.findId(id)\n}\nstatic async updateOne(id: string, title: string, coverImage: string, content: string) {\n  return await this.blogRepo.update(id, { title, coverImage, content })\n}\nstatic async deleteOne(id:string) {\n  return await this.blogRepo.delete(id)\n}\n",[774,1127,1125],{"__ignoreMap":776},[682,1129,1050,1130,1133,1134,1136,1137,1139,1140,1143,1144,1147],{},[774,1131,1132],{},"BackendMethod"," decorator and ",[774,1135,815],{},"object. The ",[774,1138,1132],{},"decorator tells Remult to expose the methods we defined in the ",[774,1141,1142],{},"BlogsController"," as API endpoints. Then, we used the ",[774,1145,1146],{},"remult.repo","method to create a repository for our Blogs entity. This provides us with the methods we need to perform database CRUD operations in each controller method.",[682,1149,1150,1151,1153,1154,1156,1157,1159],{},"Next, you also need to register the ",[774,1152,1142],{}," like you did for the ",[774,1155,1063],{},"entity in the ",[774,1158,900],{}," file. This can done with the code snippet below:",[767,1161,1164],{"className":1162,"code":1163,"language":772},[770],"import { BlogsController } from '.\u002Fshared\u002FblogController';\napp.use(remultExpress({\n  dataProvider: async () => {\n    const client = new MongoClient(\"mongodb:\u002F\u002Flocalhost:27017\u002Flocal\");\n    await client.connect();\n    return new MongoDataProvider(client.db('test'), client);\n  },\nentities: [Blogs],\ncontrollers: [BlogsController]\n}));\n",[774,1165,1163],{"__ignoreMap":776},[686,1167,1169],{"id":1168},"handle-the-react-frontend-with-remult","Handle the React Frontend with Remult",[682,1171,1172],{},"We’re done setting up the backend part of the application. Now, let’s move to our React frontend and consume the API’s we’ve created in our backend.",[682,1174,1175,1176,1178],{},"To get started, create a controllers folder in the ",[806,1177,852],{}," to create some React controllers for your application.",[682,1180,1181,1182,1185],{},"First, create a ",[806,1183,1184],{},"Form.ts"," file in the controllers folder, and add the code below:",[767,1187,1190],{"className":1188,"code":1189,"language":772},[770],"import { useState } from \"react\";\nimport { BlogsController } from \"..\u002Fserver\u002Fshared \u002FblogController\";\nexport function Form() {\n  const [title, setTitle] = useState(\"\");\n  const [coverImage, setCoverImage] = useState(\"\");\n  const [content, setContent] = useState(\"\");\n  const create = async () => {\n    await BlogsController.create(title, coverImage, content);\n};\nreturn (\n          Title              type=\"email\"      className=\"form-control\"      id=\"exampleFormControlInput1\"      onChange={(e) => setTitle(e.target.value)}    \u002F>            Cover Image              type=\"email\"      className=\"form-control\"      id=\"exampleFormControlInput1\"      onChange={(e) => setCoverImage(e.target.value)}    \u002F>            Content              className=\"form-control\"      id=\"exampleFormControlTextarea1\"      rows=\"3\"      onChange={(e) => setContent(e.target.value)}    >         create()}>Add  \n);\n}\n",[774,1191,1189],{"__ignoreMap":776},[682,1193,1194,1195,1197,1198,753,1201,1204,1205,1208,1209,1212,1213,1215],{},"In the above code snippet, we imported the ",[774,1196,1142],{}," class so we can access the API endpoints that we defined there. We also created a state variable to store the blog ",[774,1199,1200],{},"title",[774,1202,1203],{},"coverImage"," ",[774,1206,1207],{},"content","values from the form data. We made a",[774,1210,1211],{},"create","function and called the create endpoint from our ",[774,1214,1142],{}," class, passing the variables we defined for the form values to create a new blog.",[682,1217,1218,1219,1222],{},"We need this form to display in a modal when clicked. Therefore, we need to create a Modal.tsx file in the controllers file to create a ",[774,1220,1221],{},"Modal"," component. This can be done by using the code snippet below:",[767,1224,1227],{"className":1225,"code":1226,"language":772},[770],"import { Form } from \".\u002FForm\";\nexport function Modal() {\nreturn (\n  \n    className=\"modal fade\"\n    id=\"staticBackdrop\"\n    data-bs-backdrop=\"static\"\n    data-bs-keyboard=\"false\"\n    tabIndex=\"-1\"\n    aria-labelledby=\"staticBackdropLabel\"\n    aria-hidden=\"true\"\n  >\n                   Create New Blog                type=\"button\"      className=\"btn-close\"      data-bs-dismiss=\"modal\"      aria-label=\"Close\"    >                     \n);\n}\n",[774,1228,1226],{"__ignoreMap":776},[682,1230,1231,1232,1235,1236,1239],{},"In the above code, we used the ",[774,1233,1234],{},"Bootstrap"," classes to create a modal for the form component. Then, we imported the ",[774,1237,1238],{},"Form"," component and rendered it in the modal body.",[682,1241,1242],{},"Next, we’ll need to add a clickable button to show the modal component we just created. To add a button in the header part of the application, create a Header.ts file in the controllers folder and add the code below:",[767,1244,1247],{"className":1245,"code":1246,"language":772},[770],"import { Modal } from \".\u002FModal\";\nexport function Header() {\nreturn (\n    Blog App          className=\"btn btn-primary\"      data-bs-toggle=\"modal\"      data-bs-target=\"#staticBackdrop\"    >    Add New       \n);\n}\n",[774,1248,1246],{"__ignoreMap":776},[682,1250,1251,1252,1255,1256,1259],{},"Now, let’s update the code in the",[774,1253,1254],{},"App.tsx"," file to make the ",[774,1257,1258],{},"Header"," component available, read the blog data from our Remult backend, and render it to users with the following:",[767,1261,1264],{"className":1262,"code":1263,"language":772},[770],"import { useEffect, useState } from \"react\";\nimport { Header } from \".\u002Fcomponents\u002FHeader\";\nimport { BlogsController } from \".\u002Fserver\u002Fshared \u002FblogController\";\nimport { Blogs } from \".\u002Fserver\u002Fshared \u002Fblog\";\nfunction App() {\n  const [blogs, setBlogs] = useState([]);\n  useEffect(() => {\n    const fetchData = async () => {\n    const blogData = await BlogsController.getAll();\n    setBlogs(blogData);\n  };\n  fetchData();\n});\nconst deleteBlog = async (id:string)=>{\n  await BlogsController.deleteOne(id)\n}\nreturn (\n                {blogs &&        blogs.map((blog: Blogs) => {      return (                              src={blog.coverImage}            className=\"card-img-top\"            alt=\"...\"          \u002F>                  {blog.title}                        {blog.content}                        {               deleteBlog(blog.id)            }}>            Delete                                );    })}      \n);\n}\nexport default App;\n",[774,1265,1263],{"__ignoreMap":776},[682,1267,1268,1269,1272,1273,1275],{},"Also, with the above code snippet, we created a delete function to delete blogs from our database by calling the ",[774,1270,1271],{},"deleteOne"," endpoint using the ",[774,1274,1142],{},"controller class.",[682,1277,1278,1279,1282],{},"Next, open your browser and navigate to ",[730,1280,934],{"href":932,"rel":1281},[734],", and you should see the output on the screenshot below:",[682,1284,1285],{},[1286,1287],"img",{"alt":1288,"src":1289},"The first glimpse of our Remult app.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fremult-typescript-react-fullstack-app\u002Fimg-001.png",[682,1291,1292],{},"Click the Add New button to create a new blog.",[682,1294,1295],{},[1286,1296],{"alt":1297,"src":1298},"Creating a new blog about fullstack development.","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fremult-typescript-react-fullstack-app\u002Fimg-002.png",[686,1300,1302],{"id":1301},"conclusion-and-resources","Conclusion and Resources",[682,1304,1305],{},"In this tutorial, we went through a React TypesScript tutorial to build a full-stack application using Remult. We began by learning what Remult is and why a developer would use it to build full-stack web applications. Then, we built a blog application with CRUD operations as a demonstration.",[682,1307,1308,1309,1314],{},"Now that you’ve learned about Remult, how would you use it in your next project? To learn more about Remult, check out the official ",[730,1310,1313],{"href":1311,"rel":1312},"https:\u002F\u002Fremult.dev\u002F",[734],"documentation",".",{"title":776,"searchDepth":1316,"depth":1316,"links":1317},2,[1318,1322,1323,1328,1329,1330,1331],{"id":688,"depth":1316,"text":689,"children":1319},[1320],{"id":696,"depth":1321,"text":697},3,{"id":719,"depth":1316,"text":720},{"id":761,"depth":1316,"text":762,"children":1324},[1325,1326,1327],{"id":800,"depth":1321,"text":801},{"id":832,"depth":1321,"text":833},{"id":977,"depth":1321,"text":978},{"id":1027,"depth":1316,"text":1028},{"id":1103,"depth":1316,"text":1104},{"id":1168,"depth":1316,"text":1169},{"id":1301,"depth":1316,"text":1302},null,"Software Development","2022-11-01T00:00:00.000Z","Remult is a full stack TypeScript framework. It allows us to build the client and server sides of an application in a single project.",false,"md","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fremult-typescript-react-fullstack-app\u002Fhero.jpeg","Someone talking in front of an open laptop.",{},"\u002Fblog\u002Fremult-typescript-react-fullstack-app",{"title":673,"description":1335},"blog\u002Fremult-typescript-react-fullstack-app","dQ__yjAWTYp0PeB7cKGjVkRocTKm6Q89Tatr2rzN0qM",{"id":1346,"title":674,"body":1347,"description":1351,"draft":1336,"extension":1337,"meta":1354,"navigation":32,"path":1355,"photo":1356,"photoAlt":1332,"seo":1357,"stem":1358,"__hash__":1359},"authors\u002Fauthors\u002Fekekenta-clinton.md",{"type":679,"value":1348,"toc":1352},[1349],[682,1350,1351],{},"Senior Technical Writer and Backend Developer with a strong foundation in computer science. Starting their journey as a computer science student eager to build a versatile skill set, Ekekenta has grown into a professional who excels at translating complex technical concepts into clear, user-friendly documentation. With expertise in backend development, they bridge the gap between development and communication, delivering impactful solutions for both technical and non-technical audiences.",{"title":776,"searchDepth":1316,"depth":1316,"links":1353},[],{},"\u002Fauthors\u002Fekekenta-clinton","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fauthors\u002Fekekenta-clinton.jpg",{"title":674,"description":1351},"authors\u002Fekekenta-clinton","-gpDwcPlkHwUst78xTY5YerTysqc4Ri6XR_k1NnO5ec",{"left":1361,"top":1361,"width":1362,"height":1362,"rotate":1361,"vFlip":1336,"hFlip":1336,"body":1363},0,24,"\u003Cpath fill=\"currentColor\" d=\"m7.825 13l4.9 4.9q.3.3.288.7t-.313.7q-.3.275-.7.288t-.7-.288l-6.6-6.6q-.15-.15-.213-.325T4.426 12t.063-.375t.212-.325l6.6-6.6q.275-.275.688-.275t.712.275q.3.3.3.713t-.3.712L7.825 11H19q.425 0 .713.288T20 12t-.288.713T19 13z\"\u002F>"]