diff --git a/Cargo.lock b/Cargo.lock
index e220fcdc378c5e2d3a31210ad5357cc8d3852f65..0298c92fdeda002da84f7919ef1de4f59e4db37a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1482,13 +1482,13 @@ checksum = "398ea4fabe40b9b0d885340a2a991a44c8a645624075ad966d21f88688e2b69e"
 
 [[package]]
 name = "flo_binding"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "711a65cd60b91114a1cecbf6a9d533c3fde6e38532506cde25bc5f7bc87d2fa0"
+version = "2.0.2"
+source = "git+https://github.com/StarArawn/flo_binding.git?rev=609883068c43dc897da2b277e91320a41892643d#609883068c43dc897da2b277e91320a41892643d"
 dependencies = [
  "desync",
  "flo_rope",
  "futures",
+ "uuid",
 ]
 
 [[package]]
diff --git a/examples/global_counter.rs b/examples/global_counter.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7c12559253cf4929c508319a41b7322fdcdfac21
--- /dev/null
+++ b/examples/global_counter.rs
@@ -0,0 +1,90 @@
+use bevy::{
+    math::Vec2,
+    prelude::{App as BevyApp, AssetServer, Commands, Res, ResMut, World},
+    window::{WindowDescriptor, Windows},
+    PipelinedDefaultPlugins,
+};
+use bevy_kayak_ui::{BevyContext, BevyKayakUIPlugin, FontMapping, UICameraBundle};
+use kayak_components::{Text, Window};
+use kayak_core::{bind, Binding, Bound, Index, MutableBound};
+use kayak_ui::components::App;
+use kayak_ui::core::{rsx, widget};
+
+#[derive(Clone, PartialEq)]
+struct GlobalCount(pub u32);
+
+#[widget]
+fn Counter(context: &mut KayakContext) {
+    let global_count = {
+        if let Ok(world) = context.get_global_state::<World>() {
+            if let Some(global_count) = world.get_resource::<Binding<GlobalCount>>() {
+                global_count.clone()
+            } else {
+                return;
+            }
+        } else {
+            return;
+        }
+    };
+
+    context.bind(&global_count);
+
+    let global_count = global_count.get().0;
+
+    rsx! {
+        <>
+            <Window position={(50.0, 50.0)} size={(300.0, 300.0)} title={"Counter Example".to_string()}>
+                <Text size={32.0} content={format!("Current Count: {}", global_count).to_string()}>{}</Text>
+            </Window>
+        </>
+    }
+}
+
+fn startup(
+    mut commands: Commands,
+    windows: Res<Windows>,
+    mut font_mapping: ResMut<FontMapping>,
+    asset_server: Res<AssetServer>,
+) {
+    commands.spawn_bundle(UICameraBundle::new());
+
+    font_mapping.add(asset_server.load("roboto.kayak_font"));
+
+    let window_size = if let Some(window) = windows.get_primary() {
+        Vec2::new(window.width(), window.height())
+    } else {
+        panic!("Couldn't find primary window!");
+    };
+
+    commands.insert_resource(bind(GlobalCount(0)));
+
+    let context = BevyContext::new(window_size.x, window_size.y, |styles, context| {
+        // Hack to trick the proc macro for right now..
+        let parent_id: Option<Index> = None;
+        rsx! {
+            <App styles={Some(styles.clone())}>
+                <Counter />
+            </App>
+        }
+    });
+    commands.insert_resource(context);
+}
+
+fn count_up(global_count: Res<Binding<GlobalCount>>) {
+    global_count.set(GlobalCount(global_count.get().0 + 1));
+}
+
+fn main() {
+    BevyApp::new()
+        .insert_resource(WindowDescriptor {
+            width: 1270.0,
+            height: 720.0,
+            title: String::from("UI Example"),
+            ..Default::default()
+        })
+        .add_plugins(PipelinedDefaultPlugins)
+        .add_plugin(BevyKayakUIPlugin)
+        .add_startup_system(startup)
+        .add_system(count_up)
+        .run();
+}
diff --git a/kayak_core/Cargo.toml b/kayak_core/Cargo.toml
index 10e71dafb74a2aea95e8a99e319082dd9625bddb..4a5cc1ad9f7a2e01fa272959713fe725777d0cb3 100644
--- a/kayak_core/Cargo.toml
+++ b/kayak_core/Cargo.toml
@@ -10,7 +10,7 @@ as-any = "0.2"
 dashmap = "4.0"
 diff-struct = "0.3"
 derivative = "2.2"
-flo_binding = "2.0.1"
+flo_binding = { git = "https://github.com/StarArawn/flo_binding.git", rev = "609883068c43dc897da2b277e91320a41892643d" }
 fontdue = "0.6"
 kayak_render_macros = { path = "../kayak_render_macros" }
 morphorm = { git = "https://github.com/geom3trik/morphorm" }
diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs
index 582f36514a408b6948cb191aee4ae87730ceb0a0..b2a60ca9a7db86cf80f701c7e02266b3473c8c10 100644
--- a/kayak_core/src/context.rs
+++ b/kayak_core/src/context.rs
@@ -5,6 +5,7 @@ use crate::{node::NodeIndex, widget_manager::WidgetManager, Event, EventType, In
 
 pub struct KayakContext {
     component_states: HashMap<crate::Index, resources::Resources>,
+    global_bindings: HashMap<crate::Index, Vec<flo_binding::Uuid>>,
     // component_state_lifetimes: DashMap<crate::Index, Vec<Box<dyn crate::Releasable>>>,
     current_id: Index,
     pub widget_manager: WidgetManager,
@@ -25,6 +26,7 @@ impl KayakContext {
     pub fn new() -> Self {
         Self {
             component_states: HashMap::new(),
+            global_bindings: HashMap::new(),
             // component_state_lifetimes: DashMap::new(),
             current_id: crate::Index::default(),
             widget_manager: WidgetManager::new(),
@@ -34,6 +36,46 @@ impl KayakContext {
         }
     }
 
+    /// Binds some global state to the current widget.
+    pub fn bind<T: Clone + PartialEq + Send + 'static>(
+        &mut self,
+        global_state: &crate::Binding<T>,
+    ) {
+        if !self.global_bindings.contains_key(&self.current_id) {
+            self.global_bindings.insert(self.current_id, vec![]);
+        }
+
+        let global_binding_ids = self.global_bindings.get_mut(&self.current_id).unwrap();
+
+        if !global_binding_ids.contains(&global_state.id) {
+            let cloned_id = self.current_id;
+            let dirty_nodes = self.widget_manager.dirty_nodes.clone();
+            let mut lifetime = global_state.when_changed(crate::notify(move || {
+                if let Ok(mut dirty_nodes) = dirty_nodes.lock() {
+                    dirty_nodes.insert(cloned_id);
+                }
+            }));
+            // TODO: Figure out how to store this so we can drop the lifetime on unbind.
+            lifetime.keep_alive();
+            global_binding_ids.push(global_state.id);
+        }
+    }
+
+    pub fn unbind<T: Clone + PartialEq + Send + 'static>(
+        &mut self,
+        global_state: &crate::Binding<T>,
+    ) {
+        if self.global_bindings.contains_key(&self.current_id) {
+            let global_binding_ids = self.global_bindings.get_mut(&self.current_id).unwrap();
+            if let Some(index) = global_binding_ids
+                .iter()
+                .position(|id| *id == global_state.id)
+            {
+                global_binding_ids.remove(index);
+            }
+        }
+    }
+
     pub fn set_current_id(&mut self, id: crate::Index) {
         self.current_id = id;
     }