diff --git a/_CHANGES.md b/_CHANGES.md
index 28856328..0c8ea7de 100644
--- a/_CHANGES.md
+++ b/_CHANGES.md
@@ -8,4 +8,5 @@
 * removed priority
 * images-plugin moved to standalone again
 * removed MarkwonPlugin#configureHtmlRenderer -> now part of HtmlPlugin
-* TagHandler now has `supportedTags()` method
\ No newline at end of file
+* TagHandler now has `supportedTags()` method
+* html is moved completely to html-plugin
\ No newline at end of file
diff --git a/app/src/main/java/io/noties/markwon/app/MarkdownRenderer.java b/app/src/main/java/io/noties/markwon/app/MarkdownRenderer.java
index 667ef6a4..8d23ca68 100644
--- a/app/src/main/java/io/noties/markwon/app/MarkdownRenderer.java
+++ b/app/src/main/java/io/noties/markwon/app/MarkdownRenderer.java
@@ -13,27 +13,24 @@ import java.util.concurrent.Future;
 
 import javax.inject.Inject;
 
+import io.noties.debug.Debug;
 import io.noties.markwon.AbstractMarkwonPlugin;
 import io.noties.markwon.Markwon;
 import io.noties.markwon.MarkwonConfiguration;
+import io.noties.markwon.app.gif.GifAwarePlugin;
 import io.noties.markwon.ext.strikethrough.StrikethroughPlugin;
 import io.noties.markwon.ext.tables.TablePlugin;
 import io.noties.markwon.ext.tasklist.TaskListPlugin;
-import io.noties.markwon.app.gif.GifAwarePlugin;
 import io.noties.markwon.html.HtmlPlugin;
 import io.noties.markwon.image.ImagesPlugin;
-import io.noties.markwon.image.data.DataUriSchemeHandler;
 import io.noties.markwon.image.file.FileSchemeHandler;
-import io.noties.markwon.image.gif.GifMediaDecoder;
 import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
-import io.noties.markwon.image.svg.SvgMediaDecoder;
 import io.noties.markwon.syntax.Prism4jTheme;
 import io.noties.markwon.syntax.Prism4jThemeDarkula;
 import io.noties.markwon.syntax.Prism4jThemeDefault;
 import io.noties.markwon.syntax.SyntaxHighlightPlugin;
 import io.noties.markwon.urlprocessor.UrlProcessor;
 import io.noties.markwon.urlprocessor.UrlProcessorRelativeToAbsolute;
-import io.noties.debug.Debug;
 import ru.noties.prism4j.Prism4j;
 
 @ActivityScope
@@ -102,12 +99,12 @@ public class MarkdownRenderer {
                         .usePlugin(ImagesPlugin.create(new ImagesPlugin.ImagesConfigure() {
                             @Override
                             public void configureImages(@NonNull ImagesPlugin plugin) {
+                                // data uri scheme handler is added automatically
+                                // SVG & GIF will be added if required dependencies are present in the classpath
+                                // default-media-decoder is also added automatically
                                 plugin
-                                        .addSchemeHandler(DataUriSchemeHandler.create())
                                         .addSchemeHandler(OkHttpNetworkSchemeHandler.create())
-                                        .addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets()))
-                                        .addMediaDecoder(GifMediaDecoder.create(false))
-                                        .addMediaDecoder(SvgMediaDecoder.create());
+                                        .addSchemeHandler(FileSchemeHandler.createWithAssets(context.getAssets()));
                             }
                         }))
                         .usePlugin(SyntaxHighlightPlugin.create(prism4j, prism4jTheme))
diff --git a/build.gradle b/build.gradle
index 17105439..daec9a93 100644
--- a/build.gradle
+++ b/build.gradle
@@ -5,7 +5,7 @@ buildscript {
     }
     dependencies {
         classpath 'com.android.tools.build:gradle:3.4.1'
-        classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
+        classpath 'com.github.ben-manes:gradle-versions-plugin:0.21.0'
     }
 }
 
diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java
index 7166aedc..4362fbfc 100644
--- a/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java
+++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonBuilderImpl.java
@@ -2,18 +2,14 @@ package io.noties.markwon;
 
 import android.content.Context;
 import android.support.annotation.NonNull;
-import android.support.annotation.Nullable;
-import android.support.annotation.VisibleForTesting;
 import android.widget.TextView;
 
 import org.commonmark.parser.Parser;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 
 import io.noties.markwon.core.MarkwonTheme;
 
@@ -106,109 +102,8 @@ class MarkwonBuilderImpl implements Markwon.Builder {
         );
     }
 
-    @VisibleForTesting
     @NonNull
-    static List<MarkwonPlugin> preparePlugins(@NonNull List<MarkwonPlugin> plugins) {
+    private static List<MarkwonPlugin> preparePlugins(@NonNull List<MarkwonPlugin> plugins) {
         return new RegistryImpl(plugins).process();
     }
-
-    // @since 4.0.0-SNAPSHOT
-    private static class RegistryImpl implements MarkwonPlugin.Registry {
-
-        private final List<MarkwonPlugin> origin;
-        private final List<MarkwonPlugin> plugins;
-        private final Set<MarkwonPlugin> pending;
-
-        RegistryImpl(@NonNull List<MarkwonPlugin> origin) {
-            this.origin = origin;
-            this.plugins = new ArrayList<>(origin.size());
-            this.pending = new HashSet<>(3);
-        }
-
-        @NonNull
-        @Override
-        public <P extends MarkwonPlugin> P require(@NonNull Class<P> plugin) {
-            return get(plugin);
-        }
-
-        @Override
-        public <P extends MarkwonPlugin> void require(
-                @NonNull Class<P> plugin,
-                @NonNull MarkwonPlugin.Action<? super P> action) {
-            action.apply(get(plugin));
-        }
-
-        @NonNull
-        List<MarkwonPlugin> process() {
-            for (MarkwonPlugin plugin : origin) {
-                configure(plugin);
-            }
-            return plugins;
-        }
-
-        private void configure(@NonNull MarkwonPlugin plugin) {
-
-            // important -> check if it's in plugins
-            //  if it is -> no need to configure (already configured)
-
-            if (!plugins.contains(plugin)) {
-
-                if (pending.contains(plugin)) {
-                    throw new IllegalStateException("Cyclic dependency chain found: " + pending);
-                }
-
-                // start tracking plugins that are pending for configuration
-                pending.add(plugin);
-
-                plugin.configure(this);
-
-                // stop pending tracking
-                pending.remove(plugin);
-
-                // check again if it's included (a child might've configured it already)
-                // add to out-collection if not already present
-                // this is a bit different from `find` method as it does check for exact instance
-                // and not a sub-type
-                if (!plugins.contains(plugin)) {
-                    plugins.add(plugin);
-                }
-            }
-        }
-
-        @NonNull
-        private <P extends MarkwonPlugin> P get(@NonNull Class<P> type) {
-
-            // check if present already in plugins
-            // find in origin, if not found -> throw, else add to out-plugins
-
-            P plugin = find(plugins, type);
-
-            if (plugin == null) {
-
-                plugin = find(origin, type);
-
-                if (plugin == null) {
-                    throw new IllegalStateException("Requested plugin is not added: " +
-                            "" + type.getName() + ", plugins: " + origin);
-                }
-
-                configure(plugin);
-            }
-
-            return plugin;
-        }
-
-        @Nullable
-        private static <P extends MarkwonPlugin> P find(
-                @NonNull List<MarkwonPlugin> plugins,
-                @NonNull Class<P> type) {
-            for (MarkwonPlugin plugin : plugins) {
-                if (type.isAssignableFrom(plugin.getClass())) {
-                    //noinspection unchecked
-                    return (P) plugin;
-                }
-            }
-            return null;
-        }
-    }
 }
diff --git a/markwon-core/src/main/java/io/noties/markwon/MarkwonConfiguration.java b/markwon-core/src/main/java/io/noties/markwon/MarkwonConfiguration.java
index 9b4ffb95..a544ef6e 100644
--- a/markwon-core/src/main/java/io/noties/markwon/MarkwonConfiguration.java
+++ b/markwon-core/src/main/java/io/noties/markwon/MarkwonConfiguration.java
@@ -4,8 +4,6 @@ import android.support.annotation.NonNull;
 
 import io.noties.markwon.core.MarkwonTheme;
 import io.noties.markwon.core.spans.LinkSpan;
-import io.noties.markwon.html.MarkwonHtmlParser;
-import io.noties.markwon.html.MarkwonHtmlRenderer;
 import io.noties.markwon.image.AsyncDrawableLoader;
 import io.noties.markwon.image.ImageSizeResolver;
 import io.noties.markwon.image.ImageSizeResolverDef;
@@ -31,8 +29,6 @@ public class MarkwonConfiguration {
     private final LinkSpan.Resolver linkResolver;
     private final UrlProcessor urlProcessor;
     private final ImageSizeResolver imageSizeResolver;
-    private final MarkwonHtmlParser htmlParser;
-    private final MarkwonHtmlRenderer htmlRenderer;
 
     // @since 3.0.0
     private final MarkwonSpansFactory spansFactory;
@@ -45,8 +41,6 @@ public class MarkwonConfiguration {
         this.urlProcessor = builder.urlProcessor;
         this.imageSizeResolver = builder.imageSizeResolver;
         this.spansFactory = builder.spansFactory;
-        this.htmlParser = builder.htmlParser;
-        this.htmlRenderer = builder.htmlRenderer;
     }
 
     @NonNull
@@ -79,16 +73,6 @@ public class MarkwonConfiguration {
         return imageSizeResolver;
     }
 
-    @NonNull
-    public MarkwonHtmlParser htmlParser() {
-        return htmlParser;
-    }
-
-    @NonNull
-    public MarkwonHtmlRenderer htmlRenderer() {
-        return htmlRenderer;
-    }
-
     /**
      * @since 3.0.0
      */
@@ -106,8 +90,6 @@ public class MarkwonConfiguration {
         private LinkSpan.Resolver linkResolver;
         private UrlProcessor urlProcessor;
         private ImageSizeResolver imageSizeResolver;
-        private MarkwonHtmlParser htmlParser;
-        private MarkwonHtmlRenderer htmlRenderer;
         private MarkwonSpansFactory spansFactory;
 
         Builder() {
@@ -122,15 +104,6 @@ public class MarkwonConfiguration {
             return this;
         }
 
-        /**
-         * @since 4.0.0-SNAPSHOT
-         */
-        @NonNull
-        public Builder htmlRenderer(@NonNull MarkwonHtmlRenderer htmlRenderer) {
-            this.htmlRenderer = htmlRenderer;
-            return this;
-        }
-
         @NonNull
         public Builder syntaxHighlight(@NonNull SyntaxHighlight syntaxHighlight) {
             this.syntaxHighlight = syntaxHighlight;
@@ -149,12 +122,6 @@ public class MarkwonConfiguration {
             return this;
         }
 
-        @NonNull
-        public Builder htmlParser(@NonNull MarkwonHtmlParser htmlParser) {
-            this.htmlParser = htmlParser;
-            return this;
-        }
-
         /**
          * @since 1.0.1
          */
@@ -177,11 +144,6 @@ public class MarkwonConfiguration {
                 asyncDrawableLoader = AsyncDrawableLoader.noOp();
             }
 
-            // @since 4.0.0-SNAPSHOT
-            if (htmlRenderer == null) {
-                htmlRenderer = MarkwonHtmlRenderer.noOp();
-            }
-
             if (syntaxHighlight == null) {
                 syntaxHighlight = new SyntaxHighlightNoOp();
             }
@@ -198,10 +160,6 @@ public class MarkwonConfiguration {
                 imageSizeResolver = new ImageSizeResolverDef();
             }
 
-            if (htmlParser == null) {
-                htmlParser = MarkwonHtmlParser.noOp();
-            }
-
             return new MarkwonConfiguration(this);
         }
     }
diff --git a/markwon-core/src/main/java/io/noties/markwon/RegistryImpl.java b/markwon-core/src/main/java/io/noties/markwon/RegistryImpl.java
new file mode 100644
index 00000000..17a85dc8
--- /dev/null
+++ b/markwon-core/src/main/java/io/noties/markwon/RegistryImpl.java
@@ -0,0 +1,118 @@
+package io.noties.markwon;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import io.noties.markwon.core.CorePlugin;
+
+// @since 4.0.0-SNAPSHOT
+class RegistryImpl implements MarkwonPlugin.Registry {
+
+    // todo: core-plugin must be (better be) first any way
+
+    private final List<MarkwonPlugin> origin;
+    private final List<MarkwonPlugin> plugins;
+    private final Set<MarkwonPlugin> pending;
+
+    RegistryImpl(@NonNull List<MarkwonPlugin> origin) {
+        this.origin = origin;
+        this.plugins = new ArrayList<>(origin.size());
+        this.pending = new HashSet<>(3);
+    }
+
+    @NonNull
+    @Override
+    public <P extends MarkwonPlugin> P require(@NonNull Class<P> plugin) {
+        return get(plugin);
+    }
+
+    @Override
+    public <P extends MarkwonPlugin> void require(
+            @NonNull Class<P> plugin,
+            @NonNull MarkwonPlugin.Action<? super P> action) {
+        action.apply(get(plugin));
+    }
+
+    @NonNull
+    List<MarkwonPlugin> process() {
+        for (MarkwonPlugin plugin : origin) {
+            configure(plugin);
+        }
+        return plugins;
+    }
+
+    private void configure(@NonNull MarkwonPlugin plugin) {
+
+        // important -> check if it's in plugins
+        //  if it is -> no need to configure (already configured)
+
+        if (!plugins.contains(plugin)) {
+
+            if (pending.contains(plugin)) {
+                throw new IllegalStateException("Cyclic dependency chain found: " + pending);
+            }
+
+            // start tracking plugins that are pending for configuration
+            pending.add(plugin);
+
+            plugin.configure(this);
+
+            // stop pending tracking
+            pending.remove(plugin);
+
+            // check again if it's included (a child might've configured it already)
+            // add to out-collection if not already present
+            // this is a bit different from `find` method as it does check for exact instance
+            // and not a sub-type
+            if (!plugins.contains(plugin)) {
+                // core-plugin must always be the first one (if it's present)
+                if (CorePlugin.class.isAssignableFrom(plugin.getClass())) {
+                    plugins.add(0, plugin);
+                } else {
+                    plugins.add(plugin);
+                }
+            }
+        }
+    }
+
+    @NonNull
+    private <P extends MarkwonPlugin> P get(@NonNull Class<P> type) {
+
+        // check if present already in plugins
+        // find in origin, if not found -> throw, else add to out-plugins
+
+        P plugin = find(plugins, type);
+
+        if (plugin == null) {
+
+            plugin = find(origin, type);
+
+            if (plugin == null) {
+                throw new IllegalStateException("Requested plugin is not added: " +
+                        "" + type.getName() + ", plugins: " + origin);
+            }
+
+            configure(plugin);
+        }
+
+        return plugin;
+    }
+
+    @Nullable
+    private static <P extends MarkwonPlugin> P find(
+            @NonNull List<MarkwonPlugin> plugins,
+            @NonNull Class<P> type) {
+        for (MarkwonPlugin plugin : plugins) {
+            if (type.isAssignableFrom(plugin.getClass())) {
+                //noinspection unchecked
+                return (P) plugin;
+            }
+        }
+        return null;
+    }
+}
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParserNoOp.java b/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParserNoOp.java
deleted file mode 100644
index 9057630b..00000000
--- a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParserNoOp.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package io.noties.markwon.html;
-
-import android.support.annotation.NonNull;
-
-import java.util.Collections;
-
-class MarkwonHtmlParserNoOp extends MarkwonHtmlParser {
-    @Override
-    public <T extends Appendable & CharSequence> void processFragment(@NonNull T output, @NonNull String htmlFragment) {
-        // no op
-    }
-
-    @Override
-    public void flushInlineTags(int documentLength, @NonNull FlushAction<HtmlTag.Inline> action) {
-        action.apply(Collections.<HtmlTag.Inline>emptyList());
-    }
-
-    @Override
-    public void flushBlockTags(int documentLength, @NonNull FlushAction<HtmlTag.Block> action) {
-        action.apply(Collections.<HtmlTag.Block>emptyList());
-    }
-
-    @Override
-    public void reset() {
-        // no op
-    }
-}
diff --git a/markwon-core/src/test/java/io/noties/markwon/MarkwonAssert.java b/markwon-core/src/test/java/io/noties/markwon/MarkwonAssert.java
new file mode 100644
index 00000000..b220c7f8
--- /dev/null
+++ b/markwon-core/src/test/java/io/noties/markwon/MarkwonAssert.java
@@ -0,0 +1,15 @@
+package io.noties.markwon;
+
+import android.support.annotation.NonNull;
+
+import org.junit.Assert;
+
+public abstract class MarkwonAssert {
+
+    public static void assertMessageContains(@NonNull Throwable t, @NonNull String contains) {
+        Assert.assertTrue(t.getMessage(), t.getMessage().contains(contains));
+    }
+
+    private MarkwonAssert() {
+    }
+}
diff --git a/markwon-core/src/test/java/io/noties/markwon/MarkwonBuilderImplTest.java b/markwon-core/src/test/java/io/noties/markwon/MarkwonBuilderImplTest.java
index 84a3a38e..bc0fd409 100644
--- a/markwon-core/src/test/java/io/noties/markwon/MarkwonBuilderImplTest.java
+++ b/markwon-core/src/test/java/io/noties/markwon/MarkwonBuilderImplTest.java
@@ -12,7 +12,6 @@ import org.robolectric.RuntimeEnvironment;
 import org.robolectric.annotation.Config;
 
 import io.noties.markwon.core.MarkwonTheme;
-import io.noties.markwon.html.MarkwonHtmlRenderer;
 
 import static org.hamcrest.Matchers.containsString;
 import static org.junit.Assert.assertThat;
@@ -27,39 +26,6 @@ import static org.mockito.Mockito.verify;
 @Config(manifest = Config.NONE)
 public class MarkwonBuilderImplTest {
 
-    @Test
-    public void prepare_plugins() {
-        // validate that prepare plugins is calling `ensureImplicitCoreIfHasDependents` and
-        // priority processor
-
-//        final PriorityProcessor priorityProcessor = mock(PriorityProcessor.class);
-//        when(priorityProcessor.process(ArgumentMatchers.<MarkwonPlugin>anyList()))
-//                .thenAnswer(new Answer<Object>() {
-//                    @Override
-//                    public Object answer(InvocationOnMock invocation) {
-//                        return invocation.getArgument(0);
-//                    }
-//                });
-//
-//        final MarkwonPlugin plugin = new AbstractMarkwonPlugin() {
-//            @NonNull
-//            @Override
-//            public Priority priority() {
-//                return Priority.after(CorePlugin.class);
-//            }
-//        };
-//
-//        final List<MarkwonPlugin> plugins = preparePlugins(priorityProcessor, Collections.singletonList(plugin));
-//        assertThat(plugins, hasSize(2));
-//        assertThat(plugins, hasItem(plugin));
-//        assertThat(plugins, hasItem(isA(CorePlugin.class)));
-//
-//        verify(priorityProcessor, times(1))
-//                .process(ArgumentMatchers.<MarkwonPlugin>anyList());
-
-        fail();
-    }
-
     @Test
     public void no_plugins_added_throws() {
         // there is no sense in having an instance with no plugins registered
diff --git a/markwon-core/src/test/java/io/noties/markwon/RegistryImplTest.java b/markwon-core/src/test/java/io/noties/markwon/RegistryImplTest.java
new file mode 100644
index 00000000..283a799e
--- /dev/null
+++ b/markwon-core/src/test/java/io/noties/markwon/RegistryImplTest.java
@@ -0,0 +1,194 @@
+package io.noties.markwon;
+
+import android.support.annotation.NonNull;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import io.noties.markwon.core.CorePlugin;
+
+import static io.noties.markwon.MarkwonAssert.assertMessageContains;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = Config.NONE)
+public class RegistryImplTest {
+
+    @Test
+    public void single_plugin_requires_self() {
+        // detect recursive require
+
+        final class Plugin extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(Plugin.class);
+            }
+        }
+        final MarkwonPlugin plugin = new Plugin();
+
+        final RegistryImpl impl = new RegistryImpl(Collections.singletonList(plugin));
+
+        try {
+            impl.process();
+        } catch (Throwable t) {
+            assertMessageContains(t, "Cyclic dependency chain found");
+        }
+    }
+
+    @Test
+    public void plugins_dependency_cycle() {
+
+        final Map<String, Class<? extends MarkwonPlugin>> map = new HashMap<>();
+
+        final class A extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                //noinspection ConstantConditions
+                registry.require(map.get("A"));
+            }
+        }
+
+        final class B extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                //noinspection ConstantConditions
+                registry.require(map.get("B"));
+            }
+        }
+
+        final class C extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                //noinspection ConstantConditions
+                registry.require(map.get("C"));
+            }
+        }
+
+        map.put("A", B.class);
+        map.put("B", C.class);
+        map.put("C", A.class);
+
+        final RegistryImpl impl =
+                new RegistryImpl(Arrays.asList((MarkwonPlugin) new A(), new B(), new C()));
+
+        try {
+            impl.process();
+            fail();
+        } catch (Throwable t) {
+            assertMessageContains(t, "Cyclic dependency chain found");
+        }
+    }
+
+    @Test
+    public void plugins_no_dependency_cycle() {
+
+        final class C extends AbstractMarkwonPlugin {
+        }
+
+        final class B extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(C.class);
+            }
+        }
+
+        final class A extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(B.class);
+            }
+        }
+
+        final RegistryImpl impl =
+                new RegistryImpl(Arrays.asList((MarkwonPlugin) new A(), new B(), new C()));
+
+        impl.process();
+    }
+
+    @Test
+    public void dependency_not_satisfied() {
+        // when require is called for plugin not added
+
+        final class A extends AbstractMarkwonPlugin {
+        }
+
+        final class B extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(A.class);
+            }
+        }
+
+        final RegistryImpl impl =
+                new RegistryImpl(Collections.singletonList((MarkwonPlugin) new B()));
+
+        try {
+            impl.process();
+            fail();
+        } catch (Throwable t) {
+            assertMessageContains(t, "Requested plugin is not added");
+            assertMessageContains(t, A.class.getName()); // ? if it's null for local class?
+        }
+    }
+
+    @Test
+    public void core_plugin_first() {
+        // if core-plugin is present, hen it should be the first one
+
+        final CorePlugin plugin = CorePlugin.create();
+
+        final RegistryImpl impl = new RegistryImpl(Arrays.asList(
+                mock(MarkwonPlugin.class),
+                mock(MarkwonPlugin.class),
+                plugin
+        ));
+
+        final List<MarkwonPlugin> plugins = impl.process();
+        assertEquals(3, plugins.size());
+        assertEquals(plugin, plugins.get(0));
+    }
+
+    @Test
+    public void correct_order() {
+
+        final class A extends AbstractMarkwonPlugin {
+        }
+
+        final class B extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(A.class);
+            }
+        }
+
+        final class C extends AbstractMarkwonPlugin {
+            @Override
+            public void configure(@NonNull Registry registry) {
+                registry.require(B.class);
+            }
+        }
+
+        final A a = new A();
+        final B b = new B();
+        final C c = new C();
+
+        final RegistryImpl impl = new RegistryImpl(Arrays.asList(
+                (MarkwonPlugin) c, b, a));
+
+        final List<MarkwonPlugin> plugins = impl.process();
+        assertEquals(3, plugins.size());
+        assertEquals(a, plugins.get(0));
+        assertEquals(b, plugins.get(1));
+        assertEquals(c, plugins.get(2));
+    }
+}
\ No newline at end of file
diff --git a/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java b/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java
index 526fb323..6a20ada6 100644
--- a/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java
+++ b/markwon-html/src/main/java/io/noties/markwon/html/HtmlPlugin.java
@@ -7,13 +7,13 @@ import org.commonmark.node.HtmlBlock;
 import org.commonmark.node.HtmlInline;
 import org.commonmark.node.Node;
 
-import io.noties.markwon.html.tag.ImageHandler;
 import io.noties.markwon.AbstractMarkwonPlugin;
 import io.noties.markwon.MarkwonConfiguration;
 import io.noties.markwon.MarkwonVisitor;
 import io.noties.markwon.html.tag.BlockquoteHandler;
 import io.noties.markwon.html.tag.EmphasisHandler;
 import io.noties.markwon.html.tag.HeadingHandler;
+import io.noties.markwon.html.tag.ImageHandler;
 import io.noties.markwon.html.tag.LinkHandler;
 import io.noties.markwon.html.tag.ListHandler;
 import io.noties.markwon.html.tag.StrikeHandler;
@@ -53,10 +53,13 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
     public static final float SCRIPT_DEF_TEXT_SIZE_RATIO = .75F;
 
     private final MarkwonHtmlRendererImpl.Builder builder;
+    private final MarkwonHtmlParser htmlParser;
+    private MarkwonHtmlRenderer htmlRenderer;
 
     @SuppressWarnings("WeakerAccess")
     HtmlPlugin() {
         this.builder = new MarkwonHtmlRendererImpl.Builder();
+        this.htmlParser = MarkwonHtmlParserImpl.create();
     }
 
     /**
@@ -104,6 +107,8 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
     @Override
     public void configureConfiguration(@NonNull MarkwonConfiguration.Builder configurationBuilder) {
 
+        // @since 4.0.0-SNAPSHOT we init internal html-renderer here (marks the end of configuration)
+
         final MarkwonHtmlRendererImpl.Builder builder = this.builder;
 
         if (!builder.excludeDefaults()) {
@@ -123,15 +128,17 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
             builder.addDefaultTagHandler(new HeadingHandler());
         }
 
-        configurationBuilder
-                .htmlRenderer(builder.build())
-                .htmlParser(MarkwonHtmlParserImpl.create());
+        htmlRenderer = builder.build();
     }
 
     @Override
     public void afterRender(@NonNull Node node, @NonNull MarkwonVisitor visitor) {
-        final MarkwonConfiguration configuration = visitor.configuration();
-        configuration.htmlRenderer().render(visitor, configuration.htmlParser());
+        final MarkwonHtmlRenderer htmlRenderer = this.htmlRenderer;
+        if (htmlRenderer != null) {
+            htmlRenderer.render(visitor, htmlParser);
+        } else {
+            throw new IllegalStateException("Unexpected state, html-renderer is not defined");
+        }
     }
 
     @Override
@@ -153,7 +160,7 @@ public class HtmlPlugin extends AbstractMarkwonPlugin {
 
     private void visitHtml(@NonNull MarkwonVisitor visitor, @Nullable String html) {
         if (html != null) {
-            visitor.configuration().htmlParser().processFragment(visitor.builder(), html);
+            htmlParser.processFragment(visitor.builder(), html);
         }
     }
 }
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/HtmlTag.java b/markwon-html/src/main/java/io/noties/markwon/html/HtmlTag.java
similarity index 100%
rename from markwon-core/src/main/java/io/noties/markwon/html/HtmlTag.java
rename to markwon-html/src/main/java/io/noties/markwon/html/HtmlTag.java
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java b/markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java
similarity index 91%
rename from markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java
rename to markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java
index ec3b32c8..368306a7 100644
--- a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java
+++ b/markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlParser.java
@@ -9,14 +9,6 @@ import java.util.List;
  */
 public abstract class MarkwonHtmlParser {
 
-    /**
-     * Factory method to create a `no-op` implementation (no parsing)
-     */
-    @NonNull
-    public static MarkwonHtmlParser noOp() {
-        return new MarkwonHtmlParserNoOp();
-    }
-
     public interface FlushAction<T> {
         void apply(@NonNull List<T> tags);
     }
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java b/markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java
similarity index 73%
rename from markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java
rename to markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java
index 26970f2a..f0e87f37 100644
--- a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java
+++ b/markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlRenderer.java
@@ -10,14 +10,6 @@ import io.noties.markwon.MarkwonVisitor;
  */
 public abstract class MarkwonHtmlRenderer {
 
-    /**
-     * @since 4.0.0-SNAPSHOT
-     */
-    @NonNull
-    public static MarkwonHtmlRenderer noOp() {
-        return new MarkwonHtmlRendererNoOp();
-    }
-
     public abstract void render(
             @NonNull MarkwonVisitor visitor,
             @NonNull MarkwonHtmlParser parser
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlRendererNoOp.java b/markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlRendererNoOp.java
similarity index 100%
rename from markwon-core/src/main/java/io/noties/markwon/html/MarkwonHtmlRendererNoOp.java
rename to markwon-html/src/main/java/io/noties/markwon/html/MarkwonHtmlRendererNoOp.java
diff --git a/markwon-core/src/main/java/io/noties/markwon/html/TagHandler.java b/markwon-html/src/main/java/io/noties/markwon/html/TagHandler.java
similarity index 100%
rename from markwon-core/src/main/java/io/noties/markwon/html/TagHandler.java
rename to markwon-html/src/main/java/io/noties/markwon/html/TagHandler.java
diff --git a/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java b/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java
index 4d7ad275..7a9a8df8 100644
--- a/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java
+++ b/markwon-image/src/main/java/io/noties/markwon/image/ImagesPlugin.java
@@ -3,6 +3,7 @@ package io.noties.markwon.image;
 import android.graphics.drawable.Drawable;
 import android.support.annotation.NonNull;
 import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
 import android.text.Spanned;
 import android.widget.TextView;
 
@@ -10,16 +11,16 @@ import org.commonmark.node.Image;
 
 import java.util.concurrent.ExecutorService;
 
+import io.noties.markwon.AbstractMarkwonPlugin;
+import io.noties.markwon.MarkwonConfiguration;
 import io.noties.markwon.MarkwonPlugin;
+import io.noties.markwon.MarkwonSpansFactory;
 import io.noties.markwon.image.data.DataUriSchemeHandler;
 import io.noties.markwon.image.file.FileSchemeHandler;
 import io.noties.markwon.image.gif.GifMediaDecoder;
 import io.noties.markwon.image.network.NetworkSchemeHandler;
 import io.noties.markwon.image.network.OkHttpNetworkSchemeHandler;
 import io.noties.markwon.image.svg.SvgMediaDecoder;
-import io.noties.markwon.AbstractMarkwonPlugin;
-import io.noties.markwon.MarkwonConfiguration;
-import io.noties.markwon.MarkwonSpansFactory;
 
 @SuppressWarnings({"UnusedReturnValue", "WeakerAccess"})
 public class ImagesPlugin extends AbstractMarkwonPlugin {
@@ -70,7 +71,18 @@ public class ImagesPlugin extends AbstractMarkwonPlugin {
         return plugin;
     }
 
-    private final AsyncDrawableLoaderBuilder builder = new AsyncDrawableLoaderBuilder();
+    private final AsyncDrawableLoaderBuilder builder;
+
+    // @since 4.0.0-SNAPSHOT
+    ImagesPlugin() {
+        this(new AsyncDrawableLoaderBuilder());
+    }
+
+    // @since 4.0.0-SNAPSHOT
+    @VisibleForTesting
+    ImagesPlugin(@NonNull AsyncDrawableLoaderBuilder builder) {
+        this.builder = builder;
+    }
 
     /**
      * Optional (by default new cached thread executor will be used)
diff --git a/markwon-image/src/test/java/io/noties/markwon/image/AsyncDrawableLoaderBuilderTest.java b/markwon-image/src/test/java/io/noties/markwon/image/AsyncDrawableLoaderBuilderTest.java
index 51006e38..a5861f6b 100644
--- a/markwon-image/src/test/java/io/noties/markwon/image/AsyncDrawableLoaderBuilderTest.java
+++ b/markwon-image/src/test/java/io/noties/markwon/image/AsyncDrawableLoaderBuilderTest.java
@@ -18,6 +18,7 @@ import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -203,4 +204,16 @@ public class AsyncDrawableLoaderBuilderTest {
         builder.removeMediaDecoder(media);
         assertNull(builder.mediaDecoders.get(media));
     }
+
+    @Test
+    public void cannot_build_twice() {
+
+        builder.build();
+        try {
+            builder.build();
+            fail();
+        } catch (Throwable t) {
+            assertTrue(t.getMessage(), t.getMessage().contains("has already been configured"));
+        }
+    }
 }
\ No newline at end of file
diff --git a/markwon-image/src/test/java/io/noties/markwon/image/ImagesPluginTest.java b/markwon-image/src/test/java/io/noties/markwon/image/ImagesPluginTest.java
index 27f2fbb9..f19b1563 100644
--- a/markwon-image/src/test/java/io/noties/markwon/image/ImagesPluginTest.java
+++ b/markwon-image/src/test/java/io/noties/markwon/image/ImagesPluginTest.java
@@ -27,6 +27,7 @@ import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
 import static org.mockito.Mockito.when;
 
 @RunWith(RobolectricTestRunner.class)
@@ -175,4 +176,70 @@ public class ImagesPluginTest {
         verify(textView, times(1))
                 .getTag(eq(R.id.markwon_drawables_scheduler_last_text_hashcode));
     }
+
+    @Test
+    public void methods_redirected_to_builder() {
+
+        final AsyncDrawableLoaderBuilder builder = mock(AsyncDrawableLoaderBuilder.class);
+        final ImagesPlugin plugin = new ImagesPlugin(builder);
+
+        // executor service
+        {
+            final ExecutorService executorService = mock(ExecutorService.class);
+            plugin.executorService(executorService);
+            verify(builder, times(1)).executorService(eq(executorService));
+        }
+
+        // add scheme-handler
+        {
+            final SchemeHandler schemeHandler = mock(SchemeHandler.class);
+            plugin.addSchemeHandler(schemeHandler);
+            verify(builder, times(1)).addSchemeHandler(eq(schemeHandler));
+        }
+
+        // add media-decoder
+        {
+            final MediaDecoder mediaDecoder = mock(MediaDecoder.class);
+            plugin.addMediaDecoder(mediaDecoder);
+            verify(builder, times(1)).addMediaDecoder(eq(mediaDecoder));
+        }
+
+        // default-media-decoder
+        {
+            final MediaDecoder mediaDecoder = mock(MediaDecoder.class);
+            plugin.defaultMediaDecoder(mediaDecoder);
+            verify(builder, times(1)).defaultMediaDecoder(eq(mediaDecoder));
+        }
+
+        // remove scheme-handler
+        {
+            final String scheme = "yo";
+            plugin.removeSchemeHandler(scheme);
+            verify(builder, times(1)).removeSchemeHandler(eq(scheme));
+        }
+
+        // remove media-decoder
+        {
+            final String contentType = "fa/ke";
+            plugin.removeMediaDecoder(contentType);
+            verify(builder, times(1)).removeMediaDecoder(eq(contentType));
+        }
+
+        // placeholder provider
+        {
+            final ImagesPlugin.PlaceholderProvider placeholderProvider =
+                    mock(ImagesPlugin.PlaceholderProvider.class);
+            plugin.placeholderProvider(placeholderProvider);
+            verify(builder, times(1)).placeholderProvider(eq(placeholderProvider));
+        }
+
+        // error-handler
+        {
+            final ImagesPlugin.ErrorHandler errorHandler = mock(ImagesPlugin.ErrorHandler.class);
+            plugin.errorHandler(errorHandler);
+            verify(builder, times(1)).errorHandler(eq(errorHandler));
+        }
+
+        verifyNoMoreInteractions(builder);
+    }
 }
\ No newline at end of file