[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"navigation":3,"url-settings":86,"blog-\u002Fblog\u002Freact-player-customized-controls":671,"blog-author-\u002Fblog\u002Freact-player-customized-controls":1877,"i-material-symbols:arrow-back-rounded":1892},{"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":676,"buttonUrl":676,"category":1863,"date":1864,"description":1865,"draft":1866,"editorsPick":1866,"extension":1867,"featured":1866,"image":1868,"imageAlt":1869,"meta":1870,"navigation":32,"ogImage":676,"ogImageAlt":676,"path":1871,"seo":1872,"stem":1873,"tags":1874,"__hash__":1876},"blog\u002Fblog\u002Freact-player-customized-controls.md","Developing a React Video Player with Personalized Controls","Eze Nnaemeka","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Fwhats-the-difference-between-jest-and-mocha\u002Fauthor.jpeg",null,"eze-nnaemeka",{"type":679,"value":680,"toc":1842},"minimark",[681,685,690,706,709,714,727,731,734,745,752,758,777,780,786,808,815,831,835,846,852,856,859,865,868,880,885,891,895,901,905,911,914,920,931,934,940,950,956,963,969,972,978,982,985,992,998,1004,1007,1013,1036,1042,1056,1063,1074,1080,1089,1095,1098,1104,1108,1118,1128,1134,1144,1158,1164,1170,1176,1179,1185,1188,1192,1209,1215,1234,1247,1253,1263,1269,1288,1294,1305,1311,1325,1346,1352,1380,1383,1389,1393,1396,1399,1405,1417,1423,1434,1439,1445,1472,1478,1482,1491,1494,1500,1523,1529,1538,1541,1546,1550,1557,1577,1583,1593,1596,1599,1605,1620,1626,1639,1645,1648,1654,1657,1662,1667,1671,1681,1687,1693,1699,1711,1717,1724,1730,1754,1760,1763,1769,1772,1777,1781,1784,1794,1800,1806,1812,1819,1823,1833],[682,683,684],"p",{},"Web technology has developed a lot over the years; Javascript, in particular, has played an enormous role in it. By involving React, the development and use of UI have become better and more efficient. With React, including an audio-video player on any website is easy. A simple React player plugin may assist you in performing the task in a few minutes. In this article, we'll talk about how to build a customizable React video player from scratch.",[686,687,689],"h2",{"id":688},"introduction","Introduction",[682,691,692,699,700,705],{},[693,694,698],"a",{"href":695,"rel":696},"https:\u002F\u002Fwww.npmjs.com\u002Fpackage\u002Freact-player",[697],"nofollow","React-Player"," is a React component that ",[693,701,704],{"href":702,"rel":703},"https:\u002F\u002Fpieces.app\u002Fblog\u002Fthe-encrypted-media-extension-api-and-watching-protected-video-content-on-the-web",[697],"plays audio-visual files from various URLs",", including file paths, YouTube links, Facebook links, Twitch links, SoundCloud links, Streamable links, Vimeo links, Wistia links, Mixcloud links, DailyMotion links, Kaltura links, and so on.",[682,707,708],{},"In this post, we'll use the React-Player component to build a video player (which you can install with the React Player npm), while focusing more on its functionalities than its appearance. To follow this tutorial, you'll need to have Node.js installed locally on your machine and have some familiarity with Material UI, Javascript, and React.",[710,711,713],"h3",{"id":712},"features-of-react-player","Features of React-Player",[715,716,717,721,724],"ul",{},[718,719,720],"li",{},"Customizable and easy-to-use",[718,722,723],{},"Runs on diffrent URLs, including file paths",[718,725,726],{},"Supports video looping and play back rates",[686,728,730],{"id":729},"installing-dependencies","Installing Dependencies",[682,732,733],{},"Running the following commands in the terminal allows us to start by setting up the project and installing the necessary dependencies:",[735,736,741],"pre",{"className":737,"code":739,"language":740},[738],"language-text","yarn create react-app react-video-player\ncd react-video-player\nyarn add @mui\u002Fmaterial @emotion\u002Freact @emotion\u002Fstyled @mui\u002Ficons-material react-player screenfull\nyarn start\n","text",[742,743,739],"code",{"__ignoreMap":744},"",[682,746,747,748,751],{},"Let's start by importing ",[742,749,750],{},"ReactPlayer"," into our App.js file and updating it.",[735,753,756],{"className":754,"code":755,"language":740},[738],"import ReactPlayer from \"react-player\";\nimport \".\u002FApp.css\";\nimport ReactPlayer from \"react-player\";\nimport { Container } from \"@mui\u002Fmaterial\";\nfunction App() {\n return (\n            React player                                   className=\"player\"           url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"           width=\"100%\"           height=\"100%\"           playing={true}           muted={true}         \u002F>                \n );\n}\n",[742,757,755],{"__ignoreMap":744},[682,759,760,761,764,765,768,769,772,773,776],{},"The ",[742,762,763],{},"react-player"," component is now inside a ",[742,766,767],{},"Container"," wrapper. Additionally, we supplied the link to the example video as the URL prop's value. Finally, we also provided the player with a set ",[742,770,771],{},"width","and ",[742,774,775],{},"height"," of 100%, so it will be responsive.",[682,778,779],{},"Then, add the following code to your App.css file.",[735,781,784],{"className":782,"code":783,"language":740},[738],".App {\n text-align: center;\n}\n.video_container {\n   display: flex;\n   flex-direction: column;\n   justify-content: center;\n   align-items: center;\n   width: 100%;\n}\n.player__wrapper {\n   position: relative;\n}\n.player {\n   border: 2px solid #7b2cbf;\n   object-fit: cover;\n   padding: 0;\n   margin: 0;\n}\nh2 {\n color: #7b2cbf;\n}\n",[742,785,783],{"__ignoreMap":744},[682,787,788,789,792,793,796,797,800,801,804,805,807],{},"To enable playback within our video player React, we added the ",[742,790,791],{},"playing"," and ",[742,794,795],{},"muted"," props to the preceding code. In addition, we set a value of ",[742,798,799],{},"true"," in place of the default value of ",[742,802,803],{},"false"," for the props. As a result, the ",[742,806,791],{}," footage created by the above code is shown in the picture below:",[682,809,810],{},[811,812],"img",{"alt":813,"src":814},"screenshot of react video player in browser","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-001.png",[682,816,817,818,820,821,824,825,827,828,830],{},"Observe that the video player is not interactive and lacks any controls. The ",[742,819,763],{}," package contains a prop called ",[742,822,823],{},"control"," that, by default, has a value of ",[742,826,803],{},". When the value is modified to ",[742,829,799],{},", the React player controls are automatically added.",[686,832,834],{"id":833},"react-player-functionalities","React Player Functionalities",[682,836,837,838,841,842,845],{},"Let's begin by creating a new folder in the ",[742,839,840],{},"src"," folder named ",[742,843,844],{},"Components",". Then, create the Control.jsx and Control.css files as two new files inside the newly formed folder.",[735,847,850],{"className":848,"code":849,"language":740},[738],"src\n    ├── App.css\n    ├── App.js\n    ├── Components \n             ├── Control.css\n             ├──Control.jsx\n",[742,851,849],{"__ignoreMap":744},[710,853,855],{"id":854},"lets-code","Let’s Code",[682,857,858],{},"First, we’ll import the required packages for our Control.jsx file:",[735,860,863],{"className":861,"code":862,"language":740},[738],"import React from \"react\";\nimport { makeStyles, Slider, withStyles, Button,  Tooltip,  Popover,Grid\n} from \"@material-ui\u002Fcore\";\nimport {\n FastForward,\n FastRewind,\n Pause,\n PlayArrow,\n SkipNext,\n  VolumeUp,\n} from \"@material-ui\u002Ficons\";\nimport \".\u002FControl.css\";\n",[742,864,862],{"__ignoreMap":744},[682,866,867],{},"The control.jsx file is made up of three containers:",[869,870,871,874,877],"ol",{},[718,872,873],{},"The heading container",[718,875,876],{},"The middle container (Contains the Play, Rewind and Fast forward buttons)",[718,878,879],{},"The bottom container (Contains the Slider, volume and playback rate buttons)",[881,882,884],"h4",{"id":883},"the-heading-container","The Heading Container",[735,886,889],{"className":887,"code":888,"language":740},[738],"\u003Cdiv className=\"top_container\">\u003Ch2>Video PLayer\u003C\u002Fh2>\u003C\u002Fdiv>\n",[742,890,888],{"__ignoreMap":744},[881,892,894],{"id":893},"the-middle-container","The Middle Container",[735,896,899],{"className":897,"code":898,"language":740},[738],"\u003Cdiv className=\"mid__container\">\u003Cdiv className=\"icon__btn\">\u003CFastRewind fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"icon__btn\">\u003CPause fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"icon__btn\">\u003CFastForward fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003C\u002Fdiv>\n",[742,900,898],{"__ignoreMap":744},[881,902,904],{"id":903},"the-bottom-container","The Bottom Container",[735,906,909],{"className":907,"code":908,"language":740},[738],"\u003Cdiv className=\"bottom__container\">\u003Cdiv className=\"slider__container\">\u003CPrettoSlider \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"control__box\">\u003Cdiv className=\"inner__controls\">\u003Cdiv className=\"icon__btn\">\u003CPlayArrow fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"icon__btn\">\u003CSkipNext fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"icon__btn\">\u003CVolumeUp fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003CSliderclassName={`${classes.volumeSlider}`} \u002F>\u003Cspan>5\u002F20\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>\n",[742,910,908],{"__ignoreMap":744},[682,912,913],{},"Our control.jsx React player component will be like this:",[735,915,918],{"className":916,"code":917,"language":740},[738],"const Control = () => {return(\u003Cdiv className=\"control_Container\">\u002F\u002F \u003C-- The Heading Container -->\u002F\u002F \u003C-- The Middle Container -->\u002F\u002F \u003C-- The Bottom Container -->\u003C\u002Fdiv>)}\n",[742,919,917],{"__ignoreMap":744},[682,921,922,923,926,927,930],{},"Let's discuss the ",[742,924,925],{},".control_Container"," class-named ",[742,928,929],{},"div"," element. It’s a distinct container that, if hovered above the custom video player, appears as an overlay.",[682,932,933],{},"The following are the styles for our Control.jsx file:",[735,935,938],{"className":936,"code":937,"language":740},[738],".control_Container {background-color: rgba(0, 0, 0, 0.6);position: absolute;top: 0;bottom: 0;right: 0;left: 0;flex-direction: column;z-index: 1;display: flex;justify-content: space-between;}.top_container {display: flex;align-items: center;justify-content: space-between;margin: 5px 20px;}.mid__container {display: flex;justify-content: center;align-items: center;}.icon__btn {padding: 0 10px;color: #7b2cbf;}.slider__container {\u002F* width: 100%; *\u002Fdisplay: flex;align-items: center;padding: 0 16px;}.control__box {display: flex;align-items: center;justify-content: space-between;}.inner__controls {display: flex;padding: 10px 0;align-items: center;width: 50%;}span {color: #9556cc;font-size: 0.8rem;margin-left: 10px;}.second__control {display: flex;align-items: center;}\n",[742,939,937],{"__ignoreMap":744},[682,941,942,943,792,946,949],{},"Additionally, we style some of the components by adding the Material UI's ",[742,944,945],{},"makeStyles",[742,947,948],{},"withStyles"," to our Control.",[735,951,954],{"className":952,"code":953,"language":740},[738],"const useStyles = makeStyles({volumeSlider: {width: \"100px\",color: \"#9556CC\",},bottomIcons: {color: \"#999\",padding: \"12px 8px\",\"&:hover\": {color: \"#fff\",},},});const PrettoSlider = withStyles({root: {height: \"20px\",color: \"#9556CC\",display: \"flex\",justifyContent: \"center\",alignItems: \"center\",},thumb: {height: 20,width: 20,backgroundColor: \"#9556CC\",border: \"2px solid currentColor\",marginTop: -3,marginLeft: -12,\"&:focus, &:hover, &$active\": {boxShadow: \"inherit\",},},active: {},valueLabel: {left: \"calc(-50% + 4px)\",},track: {height: 5,borderRadius: 4,width: \"100%\",},rail: {height: 5,borderRadius: 4,},})(Slider);\n",[742,955,953],{"__ignoreMap":744},[682,957,958,959,962],{},"Adding the following code to your App.js file will import React’s ",[742,960,961],{},"Controls"," component, which we also need to do:",[735,964,967],{"className":965,"code":966,"language":740},[738],"import \".\u002FApp.css\";\nimport ReactPlayer from \"react-player\";\nimport { Container } from \"@material-ui\u002Fcore\";\nimport Control from \".\u002FComponents\u002FControl\";\n\nfunction App() {\n return (\n \u003Cdiv className=\"video_container\">\n \u003Cdiv>\n \u003Ch2>React player\u003C\u002Fh2>\n \u003C\u002Fdiv>\n \u003CContainer maxWidth=\"md\" justify=\"center\">\n \u003Cdiv className=\"player__wrapper\">\n \u003CReactPlayer\n           className=\"player\"\n           url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"\n           width=\"100%\"\n           height=\"100%\"\n           playing={true}\n           muted={true}\n         \u002F>\n \u003CControl \u002F>\n \u003C\u002Fdiv>\n \u003C\u002FContainer>\n \u003C\u002Fdiv>\n );\n}\n\nexport default App;\n",[742,968,966],{"__ignoreMap":744},[682,970,971],{},"If you followed closely, our React custom video player ought to appear like this:",[682,973,974],{},[811,975],{"alt":976,"src":977},"react custom video player with purple controls","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-002.png",[686,979,981],{"id":980},"handling-play-and-pause","Handling Play and Pause",[682,983,984],{},"In this section, we'll concentrate on the capabilities and logic of the custom controls for the React player, starting with playing and pausing.",[682,986,987,988,991],{},"Navigate to the App.js file, where we'll begin by defining a state named ",[742,989,990],{},"videoState",". An object with different properties represents this state:",[735,993,996],{"className":994,"code":995,"language":740},[738],"const [videoState, setVideoState] = useState({playing: true,muted: false,volume: 0.5,played: 0,seeking: false,Buffer : true});\u002F\u002FDestructuring the properties from the videoStateconst {playing, muted, volume, playbackRate, played, seeking, buffer} = videoState\n",[742,997,995],{"__ignoreMap":744},[682,999,1000,1001,1003],{},"We destructure the ",[742,1002,990],{}," using the ES6 syntax to get its properties.",[682,1005,1006],{},"Following that, we need to create a function that toggles the play and pause capabilities. To do this, we'll spread the previous state and toggle the necessary state.",[735,1008,1011],{"className":1009,"code":1010,"language":740},[738],"const playPauseHandler = () => {\u002F\u002Fplays and pause the video (toggling)setVideoState({ ...videoState, playing: !videoState.playing });};\n",[742,1012,1010],{"__ignoreMap":744},[682,1014,1015,1016,1018,1019,792,1021,1023,1024,792,1026,1029,1030,1032,1033,1035],{},"Replace the ",[742,1017,799],{}," values for the ",[742,1020,791],{},[742,1022,795],{},"properties with the appropriate values of ",[742,1025,791],{},[742,1027,1028],{},"mute"," from the destructured ",[742,1031,990],{}," in the ",[742,1034,750],{}," component.",[735,1037,1040],{"className":1038,"code":1039,"language":740},[738],"\u003Cdiv className=\"player__wrapper\">\u003CReactPlayerclassName=\"player\"url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"width=\"100%\"height=\"100%\"playing={playing}muted={muted}\u002F>\u003CControl onPlayPause={playPauseHandler} playing={playing}\u002F>\u003C\u002Fdiv>\n",[742,1041,1039],{"__ignoreMap":744},[682,1043,760,1044,1046,1047,1049,1050,1052,1053,1055],{},[742,1045,791],{}," prop in ",[742,1048,750],{}," is used to set the value to ",[742,1051,799],{}," or ",[742,1054,803],{},", playing or pausing the video.",[682,1057,1058,1059,1062],{},"To allow the play and pause buttons to access this feature, we pass in the function to the ",[742,1060,1061],{},"Control"," component as a prop in the example above.",[682,1064,1065,1066,1069,1070,1073],{},"We’d also update our Control.jsx file by passing the ",[742,1067,1068],{},"onPlayPause"," into the ",[742,1071,1072],{},"onClick"," of our pause button.",[735,1075,1078],{"className":1076,"code":1077,"language":740},[738],"\u003Cdiv className=\"icon__btn\" onClick={onPlayPause}>\u003CPause fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\n",[742,1079,1077],{"__ignoreMap":744},[682,1081,1082,1083,1085,1086,1088],{},"The playing attribute value provided within the ",[742,1084,990],{},", which we passed as a prop to the ",[742,1087,1061],{}," component, is used to render the icon using the ternary operator conditionally. For example, the play icon should appear when the video is paused, and when it is playing, the pause icon should appear.",[735,1090,1093],{"className":1091,"code":1092,"language":740},[738],"\u003Cdiv className=\"icon__btn\" onClick={onPlayPause}>{playing ? (\u003CPause fontSize=\"medium\" \u002F>) : (\u003CPlayArrow fontSize=\"medium\" \u002F>)}{\"\"}\u003C\u002Fdiv>\n",[742,1094,1092],{"__ignoreMap":744},[682,1096,1097],{},"With the addition of these functionalities, we can easily play and pause our video, like so:",[682,1099,1100],{},[811,1101],{"alt":1102,"src":1103},"gif showing the react custom video player with purple controls in action","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-003.gif",[686,1105,1107],{"id":1106},"handling-rewind-and-fast-forward","Handling Rewind and Fast Forward",[682,1109,1110,1111,1113,1114,1117],{},"We want to fast-forward the video by 10 seconds and rewind it by 5 seconds, much like with other video players. The ",[742,1112,750],{}," must be referenced using the ",[742,1115,1116],{},"useRef"," hook to obtain the video's current time before implementing this feature.",[682,1119,1120,1121,1123,1124,1127],{},"We’ll begin by importing the ",[742,1122,1116],{}," hook and creating a reference for the ",[742,1125,1126],{},"React Player",".",[735,1129,1132],{"className":1130,"code":1131,"language":740},[738],"import ReactPlayer from \"react-player\";import { useRef } from \"react\";function App() {\u002F\u002Freact player referenceconst videoPlayerRef = useRef(null);return (\u003CReactPlayerref={videoPlayerRef} \u002F\u002Fupdating the react player refclassName=\"player\"url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"width=\"100%\"height=\"100%\"playing={playing}muted={muted}\u002F>);}export default App;\n",[742,1133,1131],{"__ignoreMap":744},[682,1135,1136,1137,1140,1141,1127],{},"Let's implement a ",[742,1138,1139],{},"rewindHandler"," function that we'll call anytime the rewind button is double-tapped. This function will use two methods for the rewind and fast-forward features obtained from the ",[742,1142,1143],{},"videoPlayerRef",[715,1145,1146,1152],{},[718,1147,1148,1151],{},[742,1149,1150],{},"seekTo",": Seek the given number of seconds, or a fraction if the amount is between 0 and 1",[718,1153,1154,1157],{},[742,1155,1156],{},"getCurrentTime()",": Returns the number of seconds that have been played",[735,1159,1162],{"className":1160,"code":1161,"language":740},[738],"const rewindHandler = () => {\u002F\u002FRewinds the video player reducing 5videoPlayerRef.current.seekTo(videoPlayerRef.current.getCurrentTime() - 5);};\nThe rewindHandler subtracts 5 seconds from the current video time, which is what you're thinking about when you think about the fastForwardHandler adding 10 seconds to the current video time.\nconst fastFowardHandler = () => {\u002F\u002FFastFowards the video player by adding 10videoPlayerRef.current.seekTo(videoPlayerRef.current.getCurrentTime() + 10);};\n",[742,1163,1161],{"__ignoreMap":744},[682,1165,1166,1167,1169],{},"Next, we pass the functions as props to the ",[742,1168,1061],{}," component:",[735,1171,1174],{"className":1172,"code":1173,"language":740},[738],"\u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward ={handleFastFoward }\u002F>\n",[742,1175,1173],{"__ignoreMap":744},[682,1177,1178],{},"Then, we receive the props and pass the two functions into their respective buttons.",[735,1180,1183],{"className":1181,"code":1182,"language":740},[738],"\u003Cdiv className=\"icon__btn\" onDoubleClick={onRewind}>\u003CFastRewind fontSize=\"medium\" \u002F>\u003C\u002Fdiv>\u003Cdiv className=\"icon__btn\">\u003CFastForward fontSize=\"medium\" onDoubleClick={onForward}\u002F>\u003C\u002Fdiv>\n",[742,1184,1182],{"__ignoreMap":744},[682,1186,1187],{},"We can now fast-forward and rewind our video using the code above. However, you'll also notice that the video player's slider doesn't move to the current time when we rewind or fast-forward the video. Let's fix that, since we're problem solvers, right?",[710,1189,1191],{"id":1190},"seek-functionality","Seek Functionality",[682,1193,760,1194,1196,1197,1200,1201,1204,1205,1208],{},[742,1195,750],{}," has an ",[742,1198,1199],{},"onProgress"," prop callback for this feature, which is a ",[742,1202,1203],{},"Callback"," that contains played and loaded progress as a fraction as well as ",[742,1206,1207],{},"playedSeconds"," and loaded seconds.",[735,1210,1213],{"className":1211,"code":1212,"language":740},[738],"const progressHandler = (state) => {if (!seeking) {setVideoState({ ...videoState, ...state });}};\n",[742,1214,1212],{"__ignoreMap":744},[682,1216,760,1217,1220,1221,1224,1225,1227,1228,1230,1231,1233],{},[742,1218,1219],{},"progressHandler"," function accepts an argument called ",[742,1222,1223],{},"state",". This ",[742,1226,1223],{}," argument indicates an object with contained states. Every time the video player seek bar (the player's timing) updates, some sets of states are altered, and new values are returned. We keep the values of the previously modified States and our ",[742,1229,990],{}," properties in this function. We want this to happen, but only when the value of the seeking property in our ",[742,1232,990],{}," is negated.",[682,1235,1236,1237,1239,1240,1243,1244,1246],{},"Next, we pass in the ",[742,1238,1219],{}," function to the ",[742,1241,1242],{},"onProgess"," prop in the ",[742,1245,750],{},":",[735,1248,1251],{"className":1249,"code":1250,"language":740},[738],"\u003CReactPlayerref={videoPlayerRef}className=\"player\"url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"width=\"100%\"height=\"100%\"playing={playing}muted={muted}onProgress = {progressHandler}\u002F>\n",[742,1252,1250],{"__ignoreMap":744},[682,1254,1255,1256,1259,1260,1262],{},"Then, to update our seek slider to the appropriate time of the video, we pass the ",[742,1257,1258],{},"played"," state that we've destructured from the ",[742,1261,990],{}," to the control component as a prop.",[735,1264,1267],{"className":1265,"code":1266,"language":740},[738],"\u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward ={handleFastFoward }played ={played}\u002F>\n",[742,1268,1266],{"__ignoreMap":744},[682,1270,760,1271,1274,1275,1277,1278,1280,1281,1284,1285,1287],{},[742,1272,1273],{},"PrettoSlider"," in our ",[742,1276,823],{}," component then has to be updated. The maximum value for our ",[742,1279,1273],{}," must be 100, and the minimum value must be set to 0. The value prop is the slider's ",[742,1282,1283],{},"current e.target.value",", which multiplies the value of the ",[742,1286,1258],{}," props by 100.",[735,1289,1292],{"className":1290,"code":1291,"language":740},[738],"\u003CPrettoSlidermin = {0}max = {100}value = {played * 100}\u002F>\n",[742,1293,1291],{"__ignoreMap":744},[682,1295,1296,1297,1300,1301,1304],{},"Furthermore, we need to write functions that will enable us to use the video player slider to look for a specific moment. To do this, we'd create a ",[742,1298,1299],{},"seekHandler"," and a ",[742,1302,1303],{},"seekMouseUpHandler"," to help us achieve this feature.",[735,1306,1309],{"className":1307,"code":1308,"language":740},[738],"const seekHandler = (e, value) => {setVideoState({ ...videoState, played: parseFloat(value) \u002F 100 });};const seekMouseUpHandler = (e, value ) => {setVideoState({ ...videoState, seeking: false });videoPlayerRef.current.seekTo(value \u002F 100);};\n",[742,1310,1308],{"__ignoreMap":744},[682,1312,1313,1314,1316,1317,1319,1320,1322,1323,1127],{},"We then pass these functions as props to the ",[742,1315,823],{}," component. The parameter given to the function is the value we immediately receive whenever the slider moves. Then, since ",[742,1318,1258],{}," only accepts values between 0 and 1, we only updated the ",[742,1321,1258],{}," value to the value argument divided by 100 when updating the ",[742,1324,990],{},[682,1326,1327,1328,1331,1332,1335,1336,1339,1340,1342,1343,1345],{},"Additionally, we gave the ",[742,1329,1330],{},"mouseSeekUpHandler"," function an argument called ",[742,1333,1334],{},"value",". Spreading the previous state and changing only the ",[742,1337,1338],{},"seeking","state value to ",[742,1341,803],{},", which allows us to update the ",[742,1344,990],{},". Then, we change the video's current time to the desired time.",[735,1347,1350],{"className":1348,"code":1349,"language":740},[738],"\u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward ={handleFastFoward }played ={played}onSeek ={seekHandler}onSeekMouseUp ={seekMouseUpHandler}\u002F>\n",[742,1351,1349],{"__ignoreMap":744},[682,1353,1354,1355,1357,1358,772,1361,1364,1367,1368,1373,1374,1376,1377,1379],{},"To use these two functions, the ",[742,1356,1273],{}," has two props which we can use for this functionality: ",[742,1359,1360],{},"onChange",[742,1362,1363],{},"onChangeCommitted",[1365,1366,1127],"strong",{}," According to the ",[693,1369,1372],{"href":1370,"rel":1371},"https:\u002F\u002Fmui.com\u002Fmaterial-ui\u002Fapi\u002Fslider\u002F",[697],"MUI docs,"," the ",[742,1375,1360],{}," prop is a Callback function that fires whenever the slider's value changes, while the ",[742,1378,1363],{},"is aCallback function that fires when the mouse moves up.",[682,1381,1382],{},"This is what our video player should look like now:",[682,1384,1385],{},[811,1386],{"alt":1387,"src":1388},"gif showing react custom video player with purple controls in action","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-004.gif",[686,1390,1392],{"id":1391},"volume-functionality","Volume Functionality",[682,1394,1395],{},"We should have the option to mute and increase\u002Fdecrease the video’s volume in our React player. The icon should then change to ‘muted’ or ‘volume-up’, depending on the volume setting in that situation.",[682,1397,1398],{},"Let’s head over to our App.js file.",[735,1400,1403],{"className":1401,"code":1402,"language":740},[738],"const volumeChangeHandler = (e, value) => {const newVolume = parseFloat(value) \u002F 100;setVideoState({...videoState,volume: newVolume,muted: Number(newVolume) === 0 ? true : false, \u002F\u002F volume === 0 then muted})};const volumeSeekUpHandler = (e, value) => {const newVolume = parseFloat(value) \u002F 100;setVideoState({...videoState,volume: newVolume,muted: newVolume === 0 ? true : false,})};\n",[742,1404,1402],{"__ignoreMap":744},[682,1406,1407,1408,1410,1411,1414,1415,1127],{},"There is a volume prop for this functionality in the ",[742,1409,750],{}," where we pass the destructured ",[742,1412,1413],{},"volume","from our ",[742,1416,990],{},[735,1418,1421],{"className":1419,"code":1420,"language":740},[738],"\u003CReactPlayerref={videoPlayerRef}className=\"player\"url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"width=\"100%\"height=\"100%\"playing={playing}volume = {volume}muted={muted}onProgress={progressHandler}\u002F>\n",[742,1422,1420],{"__ignoreMap":744},[682,1424,1425,1426,1428,1429,1431,1432,1127],{},"The volume slider needs to define two functions, one for the ",[742,1427,1360],{}," event and the other for the ",[742,1430,1363],{}," event, just like the ",[742,1433,1273],{},[682,1435,1436,1437,1169],{},"Now, we pass them as props to the ",[742,1438,823],{},[735,1440,1443],{"className":1441,"code":1442,"language":740},[738],"\u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward={handleFastFoward}played={played}onSeek={seekHandler}onSeekMouseUp={seekMouseUpHandler}Volume={volume}onVolumeChangeHandler = {volumeChangeHandler}onVolumeSeekUp = {volumeSeekUpHandler}\u002F>\n",[742,1444,1442],{"__ignoreMap":744},[682,1446,1447,1448,1450,1451,1453,1454,1456,1457,1460,1463,1464,1467,792,1469,1471],{},"The volume slider is then updated by including new props, the ",[742,1449,1360],{}," event, the ",[742,1452,1363],{}," event, and ",[742,1455,1334],{},", and passing the ",[742,1458,1459],{},"onVolumeChangeHandler",[1365,1461,1462],{},","," ",[742,1465,1466],{},"onVolumeSeekUp",[1365,1468,1462],{},[742,1470,1413],{}," props.",[735,1473,1476],{"className":1474,"code":1475,"language":740},[738],"\u003CSliderclassName={`${classes.volumeSlider}`}onChange={onVolumeChangeHandler}value={volume * 100}onChangeCommitted={onVolumeSeekUp}\u002F>\n",[742,1477,1475],{"__ignoreMap":744},[710,1479,1481],{"id":1480},"handling-the-muted-state","Handling the Muted State",[682,1483,1484,1485,792,1487,1490],{},"We ought to be able to choose between ",[742,1486,795],{},[742,1488,1489],{},"volume up"," whenever the mute button is clicked. The symbol should switch to ‘muted’ or ‘volume-up’ depending on the volume level.",[682,1492,1493],{},"We’ll start by writing a function that handles the mute functionality in our App.js file.",[735,1495,1498],{"className":1496,"code":1497,"language":740},[738],"const muteHandler = () => {\u002F\u002FMutes the video playersetVideoState({ ...videoState, muted: !videoState.muted });};\n",[742,1499,1497],{"__ignoreMap":744},[682,1501,1502,1503,1506,1508,1509,1511,1512,1514,1515,1517,1518,1520,1521,1035],{},"In the ",[742,1504,1505],{},"muteHandler",[1365,1507,1462],{}," we retained everything in the ",[742,1510,990],{}," and changed the ",[742,1513,795],{}," property to have a value opposite of the current ",[742,1516,795],{}," state, thereby toggling it and then passing the ",[742,1519,795],{}," props to the ",[742,1522,823],{},[735,1524,1527],{"className":1525,"code":1526,"language":740},[738],"  \u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward={handleFastFoward}played={played}onSeek={seekHandler}onSeekMouseUp={seekMouseUpHandler}volume ={volume}onVolumeChangeHandler = {volumeChangeHandler}onVolumeSeekUp = {volumeSeekUpHandler}mute = {muted}onMute = {muteHandler}\u002F>\u003Cdiv className=\"icon__btn\" onClick={onMute} >{mute ? (\u003CVolumeOff fontSize=\"medium\" \u002F>) : (\u003CVolumeUp fontSize=\"medium\" \u002F>)}\u003C\u002Fdiv>\n",[742,1528,1526],{"__ignoreMap":744},[682,1530,1531,1532,1534,1535,1537],{},"The value of the ",[742,1533,990],{}," field's ",[742,1536,795],{}," state determines how the two icons in the above code would render conditionally. It also passes in a function to the wrapper, which toggles the mute functionality.",[682,1539,1540],{},"This is how our video should function:",[682,1542,1543],{},[811,1544],{"alt":1387,"src":1545},"https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-005.gif",[710,1547,1549],{"id":1548},"video-time-functionality","Video Time Functionality",[682,1551,1552,1553,1556],{},"Video players, as we all know, typically show the video's current time. To accomplish this, we use some instance methods that ",[693,1554,1126],{"href":695,"rel":1555},[697]," offers:",[715,1558,1559,1565,1571],{},[718,1560,1561,1564],{},[742,1562,1563],{},"getCurrentTime",": Returns the number of seconds that have been played.",[718,1566,1567,1570],{},[742,1568,1569],{},"getDuration",": Returns the currently playing media’s total duration (in seconds).",[718,1572,1573,1576],{},[742,1574,1575],{},"getSecondsLoaded",": Returns the number of seconds that have been loaded.",[735,1578,1581],{"className":1579,"code":1580,"language":740},[738],"const currentTime = videoPlayerRef.current? videoPlayerRef.current.getCurrentTime(): \"00:00\";const duration = videoPlayerRef.current? videoPlayerRef.current.getDuration(): \"00:00\";\n",[742,1582,1580],{"__ignoreMap":744},[682,1584,1585,1586,792,1589,1592],{},"We still need to format the returned time according to our preferences, even though the ",[742,1587,1588],{},"currentTime",[742,1590,1591],{},"duration"," are rendered conditionally using ternary operators.",[682,1594,1595],{},"A function that formats the timing must be created. After accepting an argument, this function will return the time in a specific format.",[682,1597,1598],{},"To use the function in App.js, we create a new file called Format.js inside the component folder.",[735,1600,1603],{"className":1601,"code":1602,"language":740},[738],"export const formatTime = (time) => {\u002F\u002Fformarting duration of videoif (isNaN(time)) {return \"00:00\";}const date = new Date(time * 1000);const hours = date.getUTCHours();const minutes = date.getUTCMinutes();const seconds = date.getUTCSeconds().toString().padStart(2, \"0\");if (hours) {\u002F\u002Fif video have hoursreturn `${hours}:${minutes.toString().padStart(2, \"0\")} `;} else return `${minutes}:${seconds}`;};\n",[742,1604,1602],{"__ignoreMap":744},[682,1606,1607,1608,1611,1612,1614,1615,792,1617,1619],{},"Following that, import the ",[742,1609,1610],{},"formatTime","method found in the Format.js file. Therefore, in the App.js file, we can use it. Then, two ",[742,1613,1610],{}," calls are made, with the ",[742,1616,1588],{},[742,1618,1591],{}," being passed as arguments.",[735,1621,1624],{"className":1622,"code":1623,"language":740},[738],"const formatCurrentTime = formatTime(currentTime)const formatDuration = formatTime(duration)\n",[742,1625,1623],{"__ignoreMap":744},[682,1627,1628,1629,772,1632,1635,1636,1638],{},"We then pass the ",[742,1630,1631],{},"formatCurrentTime",[742,1633,1634],{},"formatDuration","as props to the ",[742,1637,823],{},"component.",[735,1640,1643],{"className":1641,"code":1642,"language":740},[738],"\u003CControlonPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward={handleFastFoward}played={played}onSeek={seekHandler}onSeekMouseUp={seekMouseUpHandler}volume ={volume}onVolumeChangeHandler = {volumeChangeHandler}onVolumeSeekUp = {volumeSeekUpHandler}mute = {muted}onMute = {muteHandler}duration = {formatDuration}currentTime = {formatCurrentTime}\u002F>\n",[742,1644,1642],{"__ignoreMap":744},[682,1646,1647],{},"Finally, modify the default timing value to this:",[735,1649,1652],{"className":1650,"code":1651,"language":740},[738],"\u003Cspan>{ currentTime} : {duration}\u003C\u002Fspan>\n",[742,1653,1651],{"__ignoreMap":744},[682,1655,1656],{},"Now, you can watch the length of the video as it plays and see how much time has passed. Watch the timing as the seek bar is dragged.",[682,1658,1659],{},[811,1660],{"alt":1387,"src":1661},"https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-006.gif",[682,1663,760,1664,1666],{},[742,1665,1061],{}," container div is frequently resting on the player, as you can see. Let's make it more interesting by having it resemble other video players— let’s make it visible whenever we hover on the video player.",[881,1668,1670],{"id":1669},"creating-a-disappearing-seek-bar","Creating a Disappearing Seek Bar",[682,1672,1673,1674,1676,1677,1680],{},"To achieve this feature, we also need to create a reference of the ",[742,1675,929],{},"with classname of ",[742,1678,1679],{},"control_Container","in the App.js file:",[735,1682,1685],{"className":1683,"code":1684,"language":740},[738],"const controlRef = useRef(null)\n",[742,1686,1684],{"__ignoreMap":744},[682,1688,1689,1690,1692],{},"And pass it to the ",[742,1691,823],{},"component:",[735,1694,1697],{"className":1695,"code":1696,"language":740},[738],"\u003CControlcontrolRef = {controlRef}onPlayPause={playPauseHandler}playing={playing}onRewind={rewindHandler}onForward={handleFastFoward}played={played}onSeek={seekHandler}onSeekMouseUp={seekMouseUpHandler}volume ={volume}onVolumeChangeHandler = {volumeChangeHandler}onVolumeSeekUp = {volumeSeekUpHandler}mute = {muted}onMute = {muteHandler}playRate = {playbackRate}onPlayRate = {playBackRateHandler}duration = {formatDuration}currentTime = {formatCurrentTime}\u002F>\n",[742,1698,1696],{"__ignoreMap":744},[682,1700,1701,1702,1705,1706,1708,1709,1692],{},"We then pass it to the ",[742,1703,1704],{},"ref"," property on the ",[742,1707,1679],{},"div in the ",[742,1710,823],{},[735,1712,1715],{"className":1713,"code":1714,"language":740},[738],"const Control = ({controlRef}) => {return (\u003Cdiv className=\"control_Container\" ref ={controlRef}>\u002F\u002F \u003C-- body of the control component --->\u003C\u002Fdiv>);};export default Control;\n",[742,1716,1714],{"__ignoreMap":744},[682,1718,1719,1720,1723],{},"Next, we head over to our App.js and declare a new variable called ",[742,1721,1722],{},"count","which will be equal to 0:",[735,1725,1728],{"className":1726,"code":1727,"language":740},[738],"Let count = 0const App = () => {return (\u003Cdiv>\u002F\u002F \u003C-- body of the App component --->\u003C\u002Fdiv>);};export default App;\n",[742,1729,1727],{"__ignoreMap":744},[682,1731,1732,1733,1736,1737,1739,1740,1742,1743,1746,1747,1750,1751,1753],{},"We include an ",[742,1734,1735],{},"if"," statement inside the ",[742,1738,1219],{}," function to determine whether the ",[742,1741,1722],{}," exceeds 3, at which point we use the ",[742,1744,1745],{},"controlRef","and set the visibility to ",[742,1748,1749],{},"hidden",". By doing so, the ",[742,1752,823],{}," component div is hidden.",[735,1755,1758],{"className":1756,"code":1757,"language":740},[738],"const progressHandler = (state) => {if (count > 3){\u002F\u002F toggling player control containercontrolRef.current.style.visibility = \"hidden\";} else if (controlRef.current.style.visibility === \"visible\") {count += 1;}if (!seeking) {setVideoState({ ...videoState, ...state });}};\n",[742,1759,1757],{"__ignoreMap":744},[682,1761,1762],{},"We must create another function to make the control div visible whenever the player is hovered over.",[735,1764,1767],{"className":1765,"code":1766,"language":740},[738],"const mouseMoveHandler = () => {controlRef.current.style.visibility = \"visible\";count = 0;};const App = () => {return (\u003Cdiv>\u002F\u002F \u003C-- body of the App component --->\u003Cdiv onMouseDown = {mouseMoveHandler} >\u003CReactPlayer\u002F>\u003CControl\u002F>\u003C\u002Fdiv>\u003C\u002Fdiv>);};export default App;\n",[742,1768,1766],{"__ignoreMap":744},[682,1770,1771],{},"Our player now looks like this:",[682,1773,1774],{},[811,1775],{"alt":1387,"src":1776},"https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fimg-007.gif",[710,1778,1780],{"id":1779},"buffering-functionality-in-our-react-player","Buffering Functionality in our React Player",[682,1782,1783],{},"When we watch videos online, they frequently stall, primarily because of poor network connections. Let's add a similar feature to our video player.",[682,1785,1786,1787,1790,1791,1127],{},"The ReactPlayer provides two props that accept a callback for this feature: ",[742,1788,1789],{},"onBuffer","and",[742,1792,1793],{},"onBufferEnd",[735,1795,1798],{"className":1796,"code":1797,"language":740},[738],"const bufferStartHandler = () => {console.log(\"Bufering.......\");setVideoState({...videoState , buffer: true})};const bufferEndHandler = () => {console.log(\"buffering stoped ,,,,,,play\");setVideoState({...videoState , buffer: false})};\nThese two functions will be passed to the onBuffer and onBufferEnd.\n\u003CReactPlayerref={videoPlayerRef}className=\"player\"url=\"https:\u002F\u002Fbucket-viewer.s3.amazonaws.com\u002Fviewer1664370329252.mp4\"width=\"100%\"height=\"100%\"playing={playing}volume={volume}muted={muted}onProgress={progressHandler}playbackRate={playBackRateHandler}onBuffer={bufferStartHandler}onBufferEnd={bufferEndHandler}\u002F>\n",[742,1799,1797],{"__ignoreMap":744},[682,1801,1802,1803,1805],{},"The last step alerts users when a video freezes. The buffer state must be ",[742,1804,799],{}," to render a load effectively.",[735,1807,1810],{"className":1808,"code":1809,"language":740},[738],"{buffer &&\u003Cp>Loading\u003C\u002Fp>}\n",[742,1811,1809],{"__ignoreMap":744},[682,1813,1814,1815,1818],{},"Observe the ",[742,1816,1817],{},"Loading","text when the video freezes.",[686,1820,1822],{"id":1821},"conclusion","Conclusion",[682,1824,1825,1826,1828,1829,1832],{},"This post taught us how to construct and modify a video player using the ",[742,1827,763],{}," package and ",[742,1830,1831],{},"Material UI","to style and import the required icons. Of course, you can always improve the build by including a few special features to make the player entirely custom.",[682,1834,1835,1836,1841],{},"Here is the ",[693,1837,1840],{"href":1838,"rel":1839},"https:\u002F\u002Fgithub.com\u002Fwesley-codes\u002FReact-player.git",[697],"URL"," to the GitHub repository.",{"title":744,"searchDepth":1843,"depth":1843,"links":1844},2,[1845,1849,1850,1853,1854,1857,1862],{"id":688,"depth":1843,"text":689,"children":1846},[1847],{"id":712,"depth":1848,"text":713},3,{"id":729,"depth":1843,"text":730},{"id":833,"depth":1843,"text":834,"children":1851},[1852],{"id":854,"depth":1848,"text":855},{"id":980,"depth":1843,"text":981},{"id":1106,"depth":1843,"text":1107,"children":1855},[1856],{"id":1190,"depth":1848,"text":1191},{"id":1391,"depth":1843,"text":1392,"children":1858},[1859,1860,1861],{"id":1480,"depth":1848,"text":1481},{"id":1548,"depth":1848,"text":1549},{"id":1779,"depth":1848,"text":1780},{"id":1821,"depth":1843,"text":1822},"Software Development","2022-11-28T00:00:00.000Z","The React Player plugin can help you add a video player to your website in a few minutes. Let's we'll build a customizable React video player from scratch.",false,"md","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fblog\u002Freact-player-customized-controls\u002Fhero.jpeg","a decorative cover image showing a react video player with personalized controls",{},"\u002Fblog\u002Freact-player-customized-controls",{"title":673,"description":1865},"blog\u002Freact-player-customized-controls",[1875],"react-7; video-6e7d9; tutorial","wHO8EjWw6FtHVLbvPe3xrK4BCF17qT6g1-5v3enxtxw",{"id":1878,"title":674,"body":1879,"description":1883,"draft":1866,"extension":1867,"meta":1886,"navigation":32,"path":1887,"photo":1888,"photoAlt":676,"seo":1889,"stem":1890,"__hash__":1891},"authors\u002Fauthors\u002Feze-nnaemeka.md",{"type":679,"value":1880,"toc":1884},[1881],[682,1882,1883],{},"A highly skilled and motivated DevOps Engineer with over 3 years of experience in cloud-native distributed systems, Infrastructure as Code, and automation.",{"title":744,"searchDepth":1843,"depth":1843,"links":1885},[],{},"\u002Fauthors\u002Feze-nnaemeka","https:\u002F\u002Fstorage.googleapis.com\u002Fpieces-marketing-website\u002Fimages\u002Fauthors\u002Feze-nnaemeka.jpg",{"title":674,"description":1883},"authors\u002Feze-nnaemeka","CDxtV97RWcfH__AcOh40pIZikYNQLVog6m2M171-UvQ",{"left":1893,"top":1893,"width":1894,"height":1894,"rotate":1893,"vFlip":1866,"hFlip":1866,"body":1895},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>"]