PageSourceSearch

https://www.draftail.org/assets/js/5dc385ad.3cd9f1d0.js

js draftail.org collected 2026-09-25 20:06:14 UTC 11,210 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkdraftail_org=globalThis.webpackChunkdraftail_org||[]).push([[5884],{5920:(e,t,n)=>{n.r(t),n.d(t,{assets:()=>c,contentTitle:()=>a,default:()=>h,frontMatter:()=>s,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"importing-and-exporting-html","title":"Importing and exporting HTML","description":"Like all Draft.js editors, Draftail does not process content directly. Content storage can be done with the Draft.js representation, but it is also possible to import and export HTML instead.","source":"@site/versioned_docs/version-1.0.0/ImportingAndExportingHTML.md","sourceDirName":".","slug":"/importing-and-exporting-html","permalink":"/docs/1.0.0/importing-and-exporting-html","draft":false,"unlisted":false,"editUrl":"https://github.com/thibaudcolas/draftail.org/edit/main/versioned_docs/version-1.0.0/ImportingAndExportingHTML.md","tags":[],"version":"1.0.0","lastUpdatedBy":"Thibaud Colas","lastUpdatedAt":1763469930000,"frontMatter":{"id":"importing-and-exporting-html","title":"Importing and exporting HTML","original_id":"importing-and-exporting-html"},"sidebar":"docs","previous":{"title":"Extensions tutorial: max length","permalink":"/docs/1.0.0/extensions-tutorial-max-length"},"next":{"title":"User guide","permalink":"/docs/1.0.0/user-guide"}}');var o=n(4848),i=n(8453);const s={id:"importing-and-exporting-html",title:"Importing and exporting HTML",original_id:"importing-and-exporting-html"},a=void 0,c={},l=[{value:"Deciding how to store content",id:"deciding-how-to-store-content",level:2},{value:"Draft.js content conversion",id:"draftjs-content-conversion",level:2},{value:"HTML import",id:"html-import",level:3},{value:"HTML export",id:"html-export",level:3},{value:"Demo",id:"demo",level:2}];function d(e){const t={a:"a",blockquote:"blockquote",code:"code",h2:"h2",h3:"h3",p:"p",pre:"pre",strong:"strong",...(0,i.R)(),...e.components};return(0,o.jsxs)(o.Fragment,{children:[(0,o.jsxs)(t.p,{children:["Like all Draft.js editors, Draftail does not process content directly. ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/content-storage",children:"Content storage"})," can be done with the Draft.js representation, but it is also possible to import and export HTML instead."]}),"\n",(0,o.jsx)(t.h2,{id:"deciding-how-to-store-content",children:"Deciding how to store content"}),"\n",(0,o.jsxs)(t.p,{children:["In some use cases, it may be desirable to store rich text as HTML. This is particularly useful for websites which do not need further processing of their content when displaying it, or which already have existing HTML processing (for example in a CMS). There are also scenarios in which it might be better to store content with the Draft.js ",(0,o.jsx)(t.a,{href:"https://draftjs.org/docs/api-reference-content-state/",children:"ContentState"})," representation, for example if the content is meant to be used in different mediums (web, mobile apps, email, etc)."]}),"\n",(0,o.jsx)(t.p,{children:(0,o.jsxs)(t.strong,{children:["In either case, Draftail has no preference as long as it is provided with raw ContentState when initialised with ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/api#rawcontentstate-and-onsave",children:(0,o.jsx)(t.code,{children:"rawContentState"})}),"."]})}),"\n",(0,o.jsxs)(t.p,{children:["There are a lot of tools available to convert content. We built the Python ",(0,o.jsx)(t.a,{href:"https://github.com/springload/draftjs_exporter",children:"Draft.js exporter"})," while working on Draftail, for use in Python backends. For the purpose of this guide, we will be doing the conversion in the browser with ",(0,o.jsx)(t.a,{href:"https://github.com/HubSpot/draft-convert",children:"draft-convert"}),"."]}),"\n",(0,o.jsxs)(t.blockquote,{children:["\n",(0,o.jsxs)(t.p,{children:["There are many more converter options available on ",(0,o.jsx)(t.a,{href:"https://github.com/nikgraf/awesome-draft-js",children:"Awesome Draft.js"}),"."]}),"\n"]}),"\n",(0,o.jsx)(t.h2,{id:"draftjs-content-conversion",children:"Draft.js content conversion"}),"\n",(0,o.jsxs)(t.p,{children:["The key is to make sure that the content converters and the editor all use the same identifiers for formatting types: ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/inline-styles",children:"inline styles"}),", ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/blocks",children:"blocks"}),", and ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/entities",children:"entities"}),", and that they all preserve the same attributes/props when needed."]}),"\n",(0,o.jsxs)(t.p,{children:["For common formats, Draft.js has its predefined identifiers: ",(0,o.jsx)(t.a,{href:"https://github.com/facebook/draft-js/blob/master/src/model/constants/DraftBlockType.js",children:"block types"}),", ",(0,o.jsx)(t.a,{href:"https://github.com/facebook/draft-js/blob/master/src/model/immutable/DefaultDraftInlineStyle.js",children:"inline styles"}),", and ",(0,o.jsx)(t.code,{children:"LINK"})," and ",(0,o.jsx)(t.code,{children:"IMAGE"})," for entities. Draftail ",(0,o.jsx)(t.a,{href:"/docs/1.0.0/api#content-format-identifiers",children:"exposes the same identifiers"})," (and some more) for convenience, although using the exact same string everywhere will also work."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-tsx",children:'import { INLINE_STYLE, BLOCK_TYPE, ENTITY_TYPE } from "draftail"\n\nconsole.log(INLINE_STYLE.BOLD)\n'})}),"\n",(0,o.jsx)(t.h3,{id:"html-import",children:"HTML import"}),"\n",(0,o.jsxs)(t.p,{children:["Let's use ",(0,o.jsx)(t.a,{href:"https://github.com/HubSpot/draft-convert#convertfromhtml",children:(0,o.jsx)(t.code,{children:"convertFromHTML"})})," from ",(0,o.jsx)(t.code,{children:"draft-convert"}),". We can configure it to use the same identifiers as Draftail when convert
1ing HTML."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-tsx",children:'import { convertToRaw, convertFromRaw } from "draft-js"\nimport { convertFromHTML, convertToHTML } from "draft-convert"\nimport { DraftailEditor, ENTITY_TYPE } from "draftail"\n\nconst content = `\n<p>This editor demonstrates <strong>HTML import and export</strong>.</p>\n<hr />\n<blockquote>Built with <a href="https://github.com/HubSpot/draft-convert">draft-convert</a></blockquote>\n<img src="/static/example-lowres-image2.jpg"/>\n    <p></p>\n`\n\nconst importerConfig = {\n  htmlToEntity: (nodeName, node, createEntity) => {\n    // a tags will become LINK entities, marked as mutable, with only the URL as data.\n    if (nodeName === "a") {\n      return createEntity(ENTITY_TYPE.LINK, "MUTABLE", { url: node.href })\n    }\n\n    if (nodeName === "img") {\n      return createEntity(ENTITY_TYPE.IMAGE, "IMMUTABLE", {\n        src: node.src,\n      })\n    }\n\n    if (nodeName === "hr") {\n      return createEntity(ENTITY_TYPE.HORIZONTAL_RULE, "IMMUTABLE", {})\n    }\n\n    return null\n  },\n  htmlToBlock: (nodeName) => {\n    if (nodeName === "hr" || nodeName === "img") {\n      // "atomic" blocks is how Draft.js structures block-level entities.\n      return "atomic"\n    }\n\n    return null\n  },\n}\n\nconst fromHTML = (html) => convertToRaw(convertFromHTML(importerConfig)(html))\n\nconst editor = (\n  <DraftailEditor\n    rawContentState={fromHTML(content)}\n    enableHorizontalRule\n    entityTypes={[\n      {\n        // We use the same value for type as in the converter.\n        type: ENTITY_TYPE.LINK,\n        source: LinkSource,\n        decorator: Link,\n        // We define what data the LINKs can have.\n        attributes: ["url"],\n        whitelist: {\n          href: "^(?![#/])",\n        },\n      },\n    ]}\n  />\n)\n'})}),"\n",(0,o.jsxs)(t.p,{children:[(0,o.jsx)(t.code,{children:"convertFromHTML"})," does the heavy lifting, followed by the Draft.js ",(0,o.jsx)(t.a,{href:"https://draftjs.org/docs/api-reference-data-conversion#converttoraw",children:(0,o.jsx)(t.code,{children:"convertToRaw"})}),", and we can then initialise Draftail with HTML."]}),"\n",(0,o.jsx)(t.h3,{id:"html-export",children:"HTML export"}),"\n",(0,o.jsxs)(t.p,{children:["Converting back to HTML is the inverse process, with ",(0,o.jsx)(t.a,{href:"https://github.com/HubSpot/draft-convert#converttohtml",children:(0,o.jsx)(t.code,{children:"convertToHTML"})}),". It also needs to use the same identifiers as Draftail when converting HTML, and we will want to make sure the importer and exporter can be used in succession without altering the content."]}),"\n",(0,o.jsx)(t.pre,{children:(0,o.jsx)(t.code,{className:"language-tsx",children:'import { convertToRaw, convertFromRaw } from "draft-js"\nimport { convertFromHTML, convertToHTML } from "draft-convert"\nimport { DraftailEditor, ENTITY_TYPE } from "draftail"\n\nconst exporterConfig = {\n  blockToHTML: (block) => {\n    if (block.type === BLOCK_TYPE.BLOCKQUOTE) {\n      return <blockquote />\n    }\n\n    // Discard atomic blocks, as they get converted based on their entity.\n    if (block.type === BLOCK_TYPE.ATOMIC) {\n      return {\n        start: "",\n        end: "",\n      }\n    }\n\n    return null\n  },\n\n  entityToHTML: (entity, originalText) => {\n    if (entity.type === ENTITY_TYPE.LINK) {\n      return <a href={entity.data.url}>{originalText}</a>\n    }\n\n    if (entity.type === ENTITY_TYPE.IMAGE) {\n      return <img src={entity.data.src} alt={entity.data.alt} />\n    }\n\n    if (entity.type === ENTITY_TYPE.HORIZONTAL_RULE) {\n      return <hr />\n    }\n\n    return originalText\n  },\n}\n\nconst toHTML = (raw) =>\n  raw ? convertToHTML(exporterConfig)(convertFromRaw(raw)) : ""\n\nconst editor = (\n  <DraftailEditor\n    onSave={(raw) => {\n      console.log(toHTML(raw))\n    }}\n    enableHorizontalRule\n    entityTypes={[\n      {\n        // We use the same value for type as in the converter.\n        type: ENTITY_TYPE.LINK,\n        source: LinkSource,\n        decorator: Link,\n        // We define what data the LINKs can have.\n        attributes: ["url"],\n        whitelist: {\n          href: "^(?![#/])",\n        },\n      },\n    ]}\n  />\n)\n'})}),"\n",(0,o.jsxs)(t.p,{children:["Again, most of the configuration work is with ",(0,o.jsx)(t.code,{children:"convertToHTML"}),", but we also need the Draft.js ",(0,o.jsx)(t.a,{href:"https://draftjs.org/docs/api-reference-data-conversion#convertfromraw",children:(0,o.jsx)(t.code,{children:"convertFromRaw"})})," to read content from Draftail."]}),"\n",(0,o.jsx)(t.h2,{id:"demo",children:"Demo"}),"\n",(0,o.jsx)(t.p,{children:"Here is a demo that initialises from HTML, and converts content to HTML on save (logged in the browser DevTools console)."}),"\n",(0,o.jsx)("iframe",{src:"https://demo.draftail.org/storybook/iframe.html?id=docs--html-conversion",className:"iframe iframe--docs-400"})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,o.jsx)(t,{...e,children:(0,o.jsx)(d,{...e})}):d(e)}},8453:(e,t,n)=>{n.d(t,{R:()=>s,x:()=>a});var r=n(6540);const o={},i=r.createContext(o);function s(e){const t=r.useContext(i);return r.useMemo(function(){return"function"==typeof e?e(t):{...t,...e}},[t,e])}function a(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(o):e.components||o:s(e.components),r.createElement(i.Provider,{value:t},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.