diff --git a/reactive_graph/Cargo.toml b/reactive_graph/Cargo.toml
index 50fbc0e6c4..2db1c7e27b 100644
--- a/reactive_graph/Cargo.toml
+++ b/reactive_graph/Cargo.toml
@@ -28,6 +28,7 @@ send_wrapper = { version = "0.6.0", features = ["futures"] }
web-sys = "0.3"
[dev-dependencies]
+leptos = { workspace = true }
tokio = { version = "1", features = ["rt-multi-thread", "macros"] }
tokio-test = { version = "0.4" }
any_spawner = { workspace = true, features = ["futures-executor", "tokio"] }
diff --git a/reactive_graph/src/owner/context.rs b/reactive_graph/src/owner/context.rs
index ec84237e28..dbea18116c 100644
--- a/reactive_graph/src/owner/context.rs
+++ b/reactive_graph/src/owner/context.rs
@@ -128,6 +128,118 @@ impl Owner {
/// });
/// # });
/// ```
+///
+/// ## Warning: Shadowing Context Correctly
+///
+/// The reactive graph exists alongside the component tree. Generally
+/// speaking, context provided by a parent component can be accessed by its children
+/// and other descendants, and not vice versa. But components do not exist at
+/// runtime: a parent and children that are all rendered unconditionally exist in the same
+/// reactive scope.
+///
+/// This can have unexpected effects on context: namely, children can sometimes override
+/// contexts provided by their parents, including for their siblings, if they “shadow” context
+/// by providing another context of the same kind.
+/// ```rust
+/// use leptos::prelude::*;
+///
+/// #[component]
+/// fn Parent() -> impl IntoView {
+/// provide_context("parent_context");
+/// view! {
+/// // this is receiving "parent_context" as expected
+/// // but this is receiving "child_context" instead of "parent_context"!
+/// }
+/// }
+///
+/// #[component]
+/// fn Child() -> impl IntoView {
+/// // first, we receive context from parent (just before the override)
+/// let context = expect_context::<&'static str>();
+/// // then we provide context under the same type
+/// provide_context("child_context");
+/// view! {
+///
{format!("child (context: {context})")}
+/// }
+/// }
+/// ```
+/// In this case, neither of the children is rendered dynamically, so there is no wrapping
+/// effect created around either. All three components here have the same reactive owner, so
+/// providing a new context of the same type in the first `` overrides the context
+/// that was provided in ``, meaning that the second `` receives the context
+/// from its sibling instead.
+///
+/// ### Solution
+///
+/// If you are using the full Leptos framework, you can use the [`Provider`](leptos::context::Provider)
+/// component to solve this issue.
+///
+/// ```rust
+/// # use leptos::prelude::*;
+/// # use leptos::context::Provider;
+/// #[component]
+/// fn Child() -> impl IntoView {
+/// let context = expect_context::<&'static str>();
+/// // creates a new reactive node, which means the context will
+/// // only be provided to its children, not modified in the parent
+/// view! {
+///
+/// {format!("child (context: {context})")}
+///
+/// }
+/// }
+/// ```
+///
+/// ### Alternate Solution
+///
+/// This can also be solved by introducing some additional reactivity. In this case, it’s simplest
+/// to simply make the body of `` a function, which means it will be wrapped in a
+/// new reactive node when rendered:
+/// ```rust
+/// # use leptos::prelude::*;
+/// #[component]
+/// fn Child() -> impl IntoView {
+/// let context = expect_context::<&'static str>();
+/// // creates a new reactive node, which means the context will
+/// // only be provided to its children, not modified in the parent
+/// move || {
+/// provide_context("child_context");
+/// view! {
+/// {format!("child (context: {context})")}
+/// }
+/// }
+/// }
+/// ```
+///
+/// This is equivalent to the difference between two different forms of variable shadowing
+/// in ordinary Rust:
+/// ```rust
+/// // shadowing in a flat hierarchy overrides value for siblings
+/// // : declares variable
+/// let context = "parent_context";
+/// // First : consumes variable, then shadows
+/// println!("{context:?}");
+/// let context = "child_context";
+/// // Second : consumes variable, then shadows
+/// println!("{context:?}");
+/// let context = "child_context";
+///
+/// // but shadowing in nested scopes works as expected
+/// //
+/// let context = "parent_context";
+///
+/// // First
+/// {
+/// println!("{context:?}");
+/// let context = "child_context";
+/// }
+///
+/// // Second
+/// {
+/// println!("{context:?}");
+/// let context = "child_context";
+/// }
+/// ```
pub fn provide_context(value: T) {
if let Some(owner) = Owner::current() {
owner.provide_context(value);