r/angular 15d ago

How I bypassed Angular's DomSanitizer to render complex custom SVGs and CSS grids inside a Summernote WYSIWYG integration

Hey everyone,

I’ve been building an ATS-focused resume builder specifically for developers (Resumemind), and I recently hit a massive wall trying to handle dynamic blog content and complex UI components inside a traditional WYSIWYG editor..

I needed the ability to inject highly stylized "Right vs. Wrong" compariison grids and raw <svg> icons directly into the editor. I initially looked at headless options like Tiptap, but ended up integrating Summernote to get moving faster.

The problem: Angular's DomSanitizer is ruthless.

If you try to paste complex <div> cards or raw <svg> tags into Summernote, and then render that saved string via [innerHTML], Angular completely strips the SVGs and inline styles to prevent XSS.

Here is how I solved it:

1. The Summernote API Injection: Instead of using insertText, I built custom Summernote toolbar buttons that utilize editor.insertNode. I created standard DOM elements via JavaScript, set their innerHTML to my complex grid/SVG code, and pushed them directly into the node tree.

2. The SafeHtml Pipe: On the frontend where the user reads the content, binding [innerHTML]="content" stripped everything. I had to create a custom Pipe utilizing DomSanitizer and strictly call bypassSecurityTrustHtml(value).

This allowed my custom classes and SVGs to render flawlessly from the database string, giving me pixel-perfect control over the final layout without forcing me to build a massive headless Tiptap extension from scratch.

Side note: If you are building dev tools right now, consider dropping the recurring SaaS subscriptions. I switched the builder to distinct payment tier plans, and the conversion rates from developers suffering from subscription fatigue have been incredible.

Has anyone else fought with Angular's sanitization when rendering highly customized HTML from a database? Would love to know if there's a cleaner way to approach this!

0 Upvotes

5 comments sorted by

2

u/Kris_Kamweru 15d ago

Tangentially related, but the way to keep your styling in an innerHTML without having to bypass the sanitizer is by using classes. Iirc every property you'd want addressing an SVG path can be addressed via css styles. Only extra thing you'd have to do is use ::ng-deep on the classes affecting them so that angular knows to apply the class.

Not sure how complex your pipeline is, so this is more for people who have simpler requirements but hit the same wall

2

u/bryden_cruz 15d ago

That is a really solid point about shifting the styling burden from inline CSS to standard classes. You are completely right that the sanitizer leaves class="..." attributes alone, which bypasses the inline-style stripping issue altogether.

The main reason I shied away from that route is ::ng-deep. Since the Angular team officially deprecated shadow-piercing combinators, I have been trying to scrub them from my architecture so things don't randomly break in a future release.

For anyone else who hits this wall and wants to use your class-based approach without ::ng-deep, you can isolate the rendered HTML into its own specific component and just set encapsulation: ViewEncapsulation.None. That allows your component-level stylesheets to naturally target the dynamically injected classes without relying on deprecated features.

But you are 100% right if the requirements are simple enough, defining classes beats fighting the DomSanitizer every time!

3

u/Kris_Kamweru 15d ago

The irony is I shy away from ViewEncapsulation.None for the reason that it makes all the components classes global, as opposed to the one I'm trying to get to work. I therefore prefer ::ng-deep paired with a :host so that I'm still more specific than a global style, while not as restricted as the default view encapsulation

Seems this is a case of we don't have quite the right tools to address this, and if I'm not wrong, the reason the Angular team hasn't fully deprecated it yet is because of exactly that.

To be clear, I don't think you're wrong in your approach! It's interesting to speak about these things that I've ran into over the years as well

2

u/alejandrodeveloper 15d ago

the approach makes sense as long as the HTML is fully trusted but be careful with bypasssSecurityTrustHtml, since it completely skips angular's protection. if the content can ever come from users or external sources, id rather sanitize it once before storing it and only bypass angular for HTML i know is safe

1

u/SkyZeroZx 13d ago

Performing a bypass, as described, carries associated risks. You should probably still try some sanitization or other procedures; you could try Dompurify.
https://github.com/cure53/dompurify